加入很多新内容,这波一块交了

This commit is contained in:
FloatGaming
2026-04-08 20:53:41 +08:00
parent d9376833b6
commit 85dfff28dd
128 changed files with 14546 additions and 474 deletions
@@ -370,45 +370,7 @@ public class teamUIController : MonoBehaviour
#else
// Runtime: prefer runtimeResourcesFolderPath, fallback to selectedProjectFolderPath
string resourcesPath = ResolveResourcesRelativePath(!string.IsNullOrEmpty(runtimeResourcesFolderPath) ? runtimeResourcesFolderPath : selectedProjectFolderPath);
List<Object> runtimeDiscovered = new List<Object>();
if (!string.IsNullOrEmpty(resourcesPath))
{
resourcesPath = resourcesPath.Trim('/');
try
{
var arr = Resources.LoadAll(resourcesPath);
if (arr != null && arr.Length > 0)
runtimeDiscovered.AddRange(arr);
// If nothing loaded from the specific folder, fallback to loading all and filter by name later
if (runtimeDiscovered.Count == 0)
{
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0) // Fixed: changed arr.Length to arrAll.Length
{
runtimeDiscovered.AddRange(arrAll);
Debug.LogWarning($"[teamUIController] (Runtime) Resources.LoadAll returned 0 in '{resourcesPath}', fell back to loading all ScriptableObjects ({arrAll.Length})");
}
}
Debug.Log($"[teamUIcontroller] (Runtime) Loaded {runtimeDiscovered.Count} objects from Resources/{resourcesPath}");
}
catch (System.Exception ex)
{
Debug.LogWarning($"[teamUIController] (Runtime) Resources.LoadAll failed for '{resourcesPath}': {ex.Message}");
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0) // Fixed: changed arr.Length to arrAll.Length
runtimeDiscovered.AddRange(arrAll);
}
}
else
{
var arr = Resources.LoadAll("");
if (arr != null && arr.Length > 0)
runtimeDiscovered.AddRange(arr);
Debug.Log($"[teamUIController] (Runtime) Loaded {arr?.Length ?? 0} objects from Resources (project-wide)");
}
List<Object> runtimeDiscovered = GetOrBuildRuntimeAllyDiscovered(resourcesPath);
TeamCharacterList globalList = null;
if (teamSettingPanel.Instance != null)
@@ -549,9 +511,7 @@ public class teamUIController : MonoBehaviour
// attempt to set teammate image from underlying AllyHero_SO if possible
try
{
var ahList = Resources.LoadAll<AllyHero_SO>("");
AllyHero_SO matched = null;
foreach (var a in ahList) if (a != null && a.ally_heroID == id) { matched = a; break; }
AllyHero_SO matched = GetCachedRuntimeAllyHero(id);
if (matched != null)
{
var sprite = matched.ally_heroProfile != null ? matched.ally_heroProfile : matched.ally_heroImage;
@@ -570,6 +530,77 @@ public class teamUIController : MonoBehaviour
#endif
}
private List<Object> GetOrBuildRuntimeAllyDiscovered(string resourcesPath)
{
resourcesPath = (resourcesPath ?? string.Empty).Trim('/');
if (_runtimeAllyDiscovered != null && _runtimeAllyResourcesPath == resourcesPath)
return _runtimeAllyDiscovered;
var runtimeDiscovered = new List<Object>();
if (!string.IsNullOrEmpty(resourcesPath))
{
try
{
var arr = Resources.LoadAll(resourcesPath);
if (arr != null && arr.Length > 0)
runtimeDiscovered.AddRange(arr);
if (runtimeDiscovered.Count == 0)
{
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0)
{
runtimeDiscovered.AddRange(arrAll);
Debug.LogWarning($"[teamUIController] (Runtime) Resources.LoadAll returned 0 in '{resourcesPath}', fell back to loading all ScriptableObjects ({arrAll.Length})");
}
}
Debug.Log($"[teamUIcontroller] (Runtime) Loaded {runtimeDiscovered.Count} objects from Resources/{resourcesPath}");
}
catch (System.Exception ex)
{
Debug.LogWarning($"[teamUIController] (Runtime) Resources.LoadAll failed for '{resourcesPath}': {ex.Message}");
var arrAll = Resources.LoadAll("");
if (arrAll != null && arrAll.Length > 0)
runtimeDiscovered.AddRange(arrAll);
}
}
else
{
var arr = Resources.LoadAll("");
if (arr != null && arr.Length > 0)
runtimeDiscovered.AddRange(arr);
Debug.Log($"[teamUIController] (Runtime) Loaded {arr?.Length ?? 0} objects from Resources (project-wide)");
}
_runtimeAllyResourcesPath = resourcesPath;
_runtimeAllyDiscovered = runtimeDiscovered;
_cachedRuntimeAllyHeroes = null;
_runtimeAllyHeroById = null;
return runtimeDiscovered;
}
private AllyHero_SO GetCachedRuntimeAllyHero(int id)
{
if (id <= 0) return null;
if (_runtimeAllyHeroById != null && _runtimeAllyHeroById.TryGetValue(id, out var cached))
return cached;
if (_cachedRuntimeAllyHeroes == null)
{
_cachedRuntimeAllyHeroes = Resources.LoadAll<AllyHero_SO>("") ?? System.Array.Empty<AllyHero_SO>();
_runtimeAllyHeroById = new Dictionary<int, AllyHero_SO>();
foreach (var hero in _cachedRuntimeAllyHeroes)
{
if (hero == null) continue;
_runtimeAllyHeroById[hero.ally_heroID] = hero;
}
}
return _runtimeAllyHeroById.TryGetValue(id, out var heroFound) ? heroFound : null;
}
// ResolveResourcesRelativePath helper method is missing, assuming it exists externally or is part of a base class / extension.
// Since the user is providing the full class, I will assume it's either defined outside this snippet or is implicitly handled,
// but the code within #if UNITY_EDITOR is what matters for the file content.
@@ -815,6 +846,10 @@ public class teamUIController : MonoBehaviour
private const string EnemyDieAnimationName = "die";
private string _runtimeEnemyResourcesPath;
private List<Object> _runtimeEnemyDiscovered;
private string _runtimeAllyResourcesPath;
private List<Object> _runtimeAllyDiscovered;
private AllyHero_SO[] _cachedRuntimeAllyHeroes;
private Dictionary<int, AllyHero_SO> _runtimeAllyHeroById;
// For total enemy health bar
private int totalMaxHP = 0; // Documentation text normalized.
@@ -1031,6 +1066,8 @@ public class teamUIController : MonoBehaviour
public bool isEnemy_active = true;
[Tooltip("Documentation text normalized.")]
public Image currentEnemy_characterImage;
[Tooltip("Shown on currentEnemy_characterImage after the last enemy is defeated. If empty, falls back to the last enemy SO sprite.")]
public Sprite allEnemiesDefeatedSprite;
[Tooltip("Documentation text normalized.")]
public Image currentEnemy_healthImage;
[Tooltip("Documentation text normalized.")]
@@ -1055,6 +1092,10 @@ public class teamUIController : MonoBehaviour
public TextMeshProUGUI currentEnemy_manaRate;
[Tooltip("Documentation text normalized.")]
public TextMeshProUGUI currentEnemy_current_scoreText; // rate : now score / max score
[Tooltip("Hidden after all enemies are defeated.")]
public GameObject currentEnemy_bountyObject;
[Tooltip("Displays the current enemy bounty score.")]
public Text currentEnemy_bountyText;
[Tooltip("Documentation text normalized.")]
[SerializeField] private int enemy_id;
[Tooltip("Documentation text normalized.")]
@@ -1067,6 +1108,9 @@ public class teamUIController : MonoBehaviour
[SerializeField] private Image spine_to_put_referenceImage;
[SerializeField] private Color spineHurtFlashColor = new Color(1f, 0.2f, 0.2f, 1f);
[SerializeField] private float spineUniformHeight = 0f;
private Coroutine enemyBountyTextCoroutine;
private bool enemyBountyTransitionActive = false;
private int currentDisplayedEnemyBounty = 0;
public float SpineUniformHeight
{
get => spineUniformHeight;
@@ -1598,8 +1642,10 @@ public class teamUIController : MonoBehaviour
}
if (currentEnemy_characterImage != null)
{
// Set the sprite from SO
var sprite = so.enemy_Image != null ? so.enemy_Image : (so.enemy_Profile != null ? so.enemy_Profile : null);
// Use the configured victory sprite first, otherwise fall back to the defeated enemy sprite.
var sprite = allEnemiesDefeatedSprite != null
? allEnemiesDefeatedSprite
: (so.enemy_Image != null ? so.enemy_Image : (so.enemy_Profile != null ? so.enemy_Profile : null));
currentEnemy_characterImage.sprite = sprite;
// Apply grayscale material
@@ -1716,6 +1762,7 @@ public class teamUIController : MonoBehaviour
if (currentEnemy_manaImage != null) currentEnemy_manaImage.fillAmount = 0f;
if (currentEnemy_fademanaImage != null) currentEnemy_fademanaImage.fillAmount = 0f;
if (currentEnemy_typeText != null) currentEnemy_typeText.text = "(普通敌人)";
HideEnemyBountyUiImmediate();
UpdateEnemyListText();
// Documentation text normalized.
UpdateAllEnemyTotalHealthUIImmediate();
@@ -1756,6 +1803,8 @@ public class teamUIController : MonoBehaviour
if (currentEnemy_manaImage != null) currentEnemy_manaImage.fillAmount = 0f;
if (currentEnemy_fademanaImage != null) currentEnemy_fademanaImage.fillAmount = 0f;
if (currentEnemy_typeText != null) currentEnemy_typeText.text = "(普通敌人)";
if (!enemyBountyTransitionActive)
HideEnemyBountyUiImmediate();
UpdateEnemyListText();
// Update total health bar to 0
UpdateAllEnemyTotalHealthUIImmediate();
@@ -1834,6 +1883,23 @@ public class teamUIController : MonoBehaviour
// ensure UI parent is visible
if (objectFather_enemy != null) objectFather_enemy.SetActive(true);
int currentEnemyBounty = CalculateEnemyBountyScore(so);
if (currentEnemy_bountyObject != null)
currentEnemy_bountyObject.SetActive(true);
if (enemyCurrentCount == 0 && currentDisplayedEnemyBounty == 0 && !enemyBountyTransitionActive)
{
if (enemyBountyTextCoroutine != null)
{
StopCoroutine(enemyBountyTextCoroutine);
enemyBountyTextCoroutine = null;
}
SetEnemyBountyDisplayImmediate(currentEnemyBounty);
}
else
{
AnimateEnemyBountyRise(currentEnemyBounty);
}
// reset previous values so Update picks up initial state
prevEnemyHP = enemyCombatantInstance.currentHP;
prevEnemyMana = enemyCombatantInstance.currentMana;
@@ -1992,6 +2058,16 @@ public class teamUIController : MonoBehaviour
}
if (delay > 0f)
{
int nextIndex = enemyCurrentCount + 1;
if (nextIndex >= recognizedEnemySOs.Length)
{
AnimateEnemyBountyToZero(true);
}
else
{
AnimateEnemyBountyToZero(false);
}
if (_enemySpineDeathCoroutine != null)
{
StopCoroutine(_enemySpineDeathCoroutine);
@@ -2004,6 +2080,16 @@ public class teamUIController : MonoBehaviour
}
}
int nextEnemyIndex = enemyCurrentCount + 1;
if (nextEnemyIndex >= recognizedEnemySOs.Length)
{
AnimateEnemyBountyToZero(true);
}
else
{
AnimateEnemyBountyToZero(false);
}
enemyCurrentCount++;
SpawnNextEnemy();
}
@@ -2014,6 +2100,113 @@ public class teamUIController : MonoBehaviour
UpdateEnemyUIImmediate();
}
private int CalculateEnemyBountyScore(EnemyData_SO so)
{
if (so == null)
return 0;
int difficultyID = BeatmapManager.Instance != null ? BeatmapManager.Instance.assignedDifficulty : 0;
EnemyData_SO.EnemyDifficultyStats stats = so.GetStatsByDifficultyID(difficultyID);
int calculatedMaxHp = Mathf.Max(0, stats.enemy_maxHP);
float difficultyLevel = ResolveCurrentDifficultyLevel(difficultyID);
float totalBounty = calculatedMaxHp * 0.05f * difficultyLevel + so.enemyBountyBonus;
return Mathf.Max(0, Mathf.CeilToInt(totalBounty));
}
private float ResolveCurrentDifficultyLevel(int difficultyID)
{
BeatmapManager beatmapManager = BeatmapManager.Instance;
if (beatmapManager == null || beatmapManager.assignedSongData == null || beatmapManager.assignedSongData.chartFiles == null)
return 1f;
List<ChartFileEntry> chartFiles = beatmapManager.assignedSongData.chartFiles;
for (int i = 0; i < chartFiles.Count; i++)
{
ChartFileEntry entry = chartFiles[i];
if (entry == null || entry.difficulty != difficultyID)
continue;
return Mathf.Max(0f, entry.difficultyLEVEL);
}
return 1f;
}
private void SetEnemyBountyDisplayImmediate(int value)
{
currentDisplayedEnemyBounty = Mathf.Max(0, value);
if (currentEnemy_bountyText != null)
currentEnemy_bountyText.text = currentDisplayedEnemyBounty.ToString();
}
private void HideEnemyBountyUiImmediate()
{
if (enemyBountyTextCoroutine != null)
{
StopCoroutine(enemyBountyTextCoroutine);
enemyBountyTextCoroutine = null;
}
enemyBountyTransitionActive = false;
SetEnemyBountyDisplayImmediate(0);
if (currentEnemy_bountyObject != null)
currentEnemy_bountyObject.SetActive(false);
}
private void AnimateEnemyBountyRise(int targetValue)
{
if (enemyBountyTextCoroutine != null)
StopCoroutine(enemyBountyTextCoroutine);
if (currentEnemy_bountyObject != null)
currentEnemy_bountyObject.SetActive(true);
enemyBountyTransitionActive = true;
enemyBountyTextCoroutine = StartCoroutine(AnimateEnemyBountyValueRoutine(0, Mathf.Max(0, targetValue), 1f, false));
}
private void AnimateEnemyBountyToZero(bool hideAfterZero)
{
if (enemyBountyTextCoroutine != null)
StopCoroutine(enemyBountyTextCoroutine);
if (currentEnemy_bountyObject != null)
currentEnemy_bountyObject.SetActive(true);
enemyBountyTransitionActive = true;
enemyBountyTextCoroutine = StartCoroutine(AnimateEnemyBountyValueRoutine(currentDisplayedEnemyBounty, 0, 1f, hideAfterZero));
}
private IEnumerator AnimateEnemyBountyValueRoutine(int startValue, int endValue, float duration, bool deactivateAtEnd)
{
enemyBountyTransitionActive = true;
SetEnemyBountyDisplayImmediate(startValue);
if (duration <= 0f)
{
SetEnemyBountyDisplayImmediate(endValue);
}
else
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
int value = Mathf.RoundToInt(Mathf.Lerp(startValue, endValue, t));
SetEnemyBountyDisplayImmediate(value);
yield return null;
}
SetEnemyBountyDisplayImmediate(endValue);
}
enemyBountyTransitionActive = false;
enemyBountyTextCoroutine = null;
if (deactivateAtEnd && currentEnemy_bountyObject != null)
currentEnemy_bountyObject.SetActive(false);
}
// Documentation text normalized.
// =========================================================
@@ -2066,6 +2259,15 @@ public class teamUIController : MonoBehaviour
Debug.Log($"[teamUIController] Re-initializing current enemy instance with new HP: {stats.enemy_maxHP}");
enemyCombatantInstance.InitializeFromSO(currentSO, diffID);
UpdateEnemyUIImmediate();
// The first enemy can be spawned before BeatmapManager finishes writing
// the calculated HP back into the enemy SO. Refresh bounty text here so it
// reflects the final computed max HP rather than the pre-calculation value.
int refreshedBounty = CalculateEnemyBountyScore(currentSO);
if (currentEnemy_bountyObject != null)
currentEnemy_bountyObject.SetActive(true);
if (!enemyBountyTransitionActive)
SetEnemyBountyDisplayImmediate(refreshedBounty);
}
}
}