多轮审查和修复
This commit is contained in:
138
Assets/Scripts/Quest/ChallengeRoomManager.cs
Normal file
138
Assets/Scripts/Quest/ChallengeRoomManager.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Enemies;
|
||||
using BaseGames.Player;
|
||||
|
||||
namespace BaseGames.Challenge
|
||||
{
|
||||
/// <summary>
|
||||
/// 挑战房间流程管理器(架构 22_QuestChallengeModule §12)。
|
||||
/// 挂在挑战房间场景的 [ChallengeManager] GameObject 上,场景加载时自动启动挑战。
|
||||
/// </summary>
|
||||
public class ChallengeRoomManager : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private ChallengeRoomSO _challengeData;
|
||||
[SerializeField] private PlayerStats _player; // 由场景 Inspector 绑定
|
||||
|
||||
[Header("Event Channels")]
|
||||
[SerializeField] private StringEventChannelSO _onChallengeCompleted; // EVT_ChallengeCompleted
|
||||
[SerializeField] private StringEventChannelSO _onChallengeFailed; // EVT_ChallengeFailed
|
||||
|
||||
private int _currentEncounterIndex;
|
||||
private int _remainingEnemies;
|
||||
private float _elapsedTime;
|
||||
private bool _isRunning;
|
||||
private bool _noHitViolated; // 架构 §12:requireNoHit 挑战是否被破坏
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_player != null) _player.OnDamaged += OnPlayerDamaged;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_player != null) _player.OnDamaged -= OnPlayerDamaged;
|
||||
}
|
||||
|
||||
private void OnPlayerDamaged() => _noHitViolated = true;
|
||||
|
||||
private void Start() => StartChallenge();
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isRunning) return;
|
||||
_elapsedTime += Time.deltaTime;
|
||||
|
||||
if (_challengeData.timeLimit > 0 && _elapsedTime >= _challengeData.timeLimit)
|
||||
FailChallenge();
|
||||
}
|
||||
|
||||
private void StartChallenge()
|
||||
{
|
||||
// 自动快速存档(失败后读档返回挑战入口)
|
||||
ServiceLocator.GetOrDefault<ISaveService>()?.QuickSave();
|
||||
_isRunning = true;
|
||||
_currentEncounterIndex = 0;
|
||||
_elapsedTime = 0f;
|
||||
_noHitViolated = false;
|
||||
SpawnWave(0);
|
||||
}
|
||||
|
||||
private void SpawnWave(int index)
|
||||
{
|
||||
if (_challengeData.encounters == null || index >= _challengeData.encounters.Length)
|
||||
{
|
||||
CompleteChallenge();
|
||||
return;
|
||||
}
|
||||
|
||||
var enc = _challengeData.encounters[index];
|
||||
_remainingEnemies = 0;
|
||||
|
||||
foreach (var entry in enc.enemies)
|
||||
{
|
||||
for (int i = 0; i < entry.count; i++)
|
||||
{
|
||||
_remainingEnemies++;
|
||||
Vector3 pos = entry.spawnPoint != null ? entry.spawnPoint.position : Vector3.zero;
|
||||
Addressables.InstantiateAsync(entry.enemyAddressKey, pos, Quaternion.identity)
|
||||
.Completed += handle =>
|
||||
{
|
||||
if (handle.Result != null &&
|
||||
handle.Result.TryGetComponent<EnemyBase>(out var enemy))
|
||||
{
|
||||
enemy.OnDied += OnEnemyDefeated;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnemyDefeated()
|
||||
{
|
||||
_remainingEnemies = Mathf.Max(0, _remainingEnemies - 1);
|
||||
if (_remainingEnemies > 0) return;
|
||||
|
||||
_currentEncounterIndex++;
|
||||
if (_currentEncounterIndex >= _challengeData.encounters.Length)
|
||||
CompleteChallenge();
|
||||
else
|
||||
StartCoroutine(DelayedNextWave(_challengeData.encounters[_currentEncounterIndex].waveDelay));
|
||||
}
|
||||
|
||||
private IEnumerator DelayedNextWave(float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
SpawnWave(_currentEncounterIndex);
|
||||
}
|
||||
|
||||
private void CompleteChallenge()
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
// requireNoHit 挑战:受到伤害则判定失败(架构 §12)
|
||||
if (_challengeData.requireNoHit && _noHitViolated)
|
||||
{
|
||||
FailChallenge();
|
||||
return;
|
||||
}
|
||||
|
||||
var reward = ServiceLocator.GetOrDefault<ISaveService>() is { } sm && sm.IsFirstClear(_challengeData.challengeId)
|
||||
? _challengeData.firstClearReward
|
||||
: _challengeData.repeatedReward;
|
||||
reward?.Apply(_player);
|
||||
|
||||
_onChallengeCompleted?.Raise(_challengeData.challengeId);
|
||||
}
|
||||
|
||||
private void FailChallenge()
|
||||
{
|
||||
_isRunning = false;
|
||||
_onChallengeFailed?.Raise(_challengeData.challengeId);
|
||||
ServiceLocator.GetOrDefault<ISaveService>()?.QuickLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user