加入很多新内容,这波一块交了
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user