geometry-tower-defense/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs

324 lines
11 KiB
C#

using System.Collections.Generic;
using GeometryTD.CustomEvent;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using GeometryTD.Entity;
using GeometryTD.UI;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent
{
public partial class CombatScheduler : ICombatSchedulerHost
{
private readonly CombatSchedulerRuntimeContext _context = new();
private readonly CombatSchedulerFlowCoordinator _flowCoordinator;
private bool _initialized;
public CombatScheduler()
{
_flowCoordinator = new CombatSchedulerFlowCoordinator(this, _context);
}
public bool IsRunning =>
_context.CurrentState is CombatLoadingState or CombatRunningPhaseState or CombatWaitingForPhaseEndState;
public bool IsCompleted => _context.IsCompleted;
public DRLevel CurrentLevel => _context.CurrentLevel;
public DRLevelPhase CurrentPhase => _context.PhaseLoopRuntime.CurrentPhase;
public MapEntity CurrentMap => _context.LoadSession.CurrentMap;
public int DisplayPhaseIndex => _context.PhaseLoopRuntime.DisplayPhaseIndex;
public int PhaseCount => _context.PhaseLoopRuntime.PhaseCount;
public bool CanEndCombat => _context.PhaseLoopRuntime.CanEndCombat;
public int CurrentCoin => _context.CombatInRunResourceManager.CurrentCoin;
public int CurrentGold => _context.CombatInRunResourceManager.CurrentGold;
public int CurrentBaseHp => _context.CombatInRunResourceManager.CurrentBaseHp;
public int CurrentBuildTowerCount => _context.CombatInRunResourceManager.CurrentBuildTowerCount;
public int DefeatedEnemyCount => _context.EnemyManager.DefeatedEnemyCount;
public int GainedCoin => _context.CombatInRunResourceManager.GainedCoin;
public int GainedGold => _context.CombatInRunResourceManager.GainedGold;
public void OnInit()
{
if (!_initialized)
{
_context.Entity = GameEntry.Entity;
_context.EventBridge.Bind(
OnShowEntitySuccess,
OnShowEntityFailure,
OnHideEntityComplete,
OnOpenUIFormSuccess,
OnOpenUIFormFailure,
OnCloseUIFormComplete);
_context.EnemyManager.OnInit(this);
_context.LoadSession.OnInit(_context.Entity);
_flowCoordinator.EnsureCombatFinishFormUseCaseBound();
_flowCoordinator.EnsureRewardSelectFormUseCaseBound();
_initialized = true;
}
_flowCoordinator.ResetRuntime();
}
public bool Start(
DRLevel level,
IReadOnlyList<DRLevelPhase> phases,
IReadOnlyDictionary<int, IReadOnlyList<DRLevelSpawnEntry>> spawnEntriesByPhaseId)
{
if (!_initialized || _context.Entity == null)
{
return _flowCoordinator.HandleStartFailure("CombatScheduler start failed. Runtime is not initialized.");
}
if (level == null || phases == null || phases.Count <= 0)
{
return _flowCoordinator.HandleStartFailure("CombatScheduler start failed. Invalid level or phase data.");
}
_flowCoordinator.CleanupAllCombatEntities();
_flowCoordinator.CloseCombatFinishForm();
_flowCoordinator.CloseRewardSelectForm();
_flowCoordinator.CloseDialogForm();
_context.EnemyManager.EndPhase();
_context.EnemyManager.ResetCombatStats();
_flowCoordinator.ResetRuntime();
_context.IsFinishAsVictory = true;
_context.CurrentLevel = level;
_context.CombatInRunResourceManager.InitializeForCombat(level);
for (int i = 0; i < phases.Count; i++)
{
DRLevelPhase phase = phases[i];
if (phase != null)
{
_context.PhaseBuffer.Add(phase);
}
}
if (spawnEntriesByPhaseId != null)
{
foreach (var pair in spawnEntriesByPhaseId)
{
_context.SpawnEntriesByPhaseId[pair.Key] = pair.Value;
}
}
_context.PhaseLoopRuntime.SetPhases(_context.PhaseBuffer);
if (_context.PhaseLoopRuntime.PhaseCount <= 0)
{
return _flowCoordinator.HandleStartFailure($"CombatScheduler start failed. Level '{level.Id}' has no phase data.");
}
ChangeState(new CombatLoadingState(_context, _flowCoordinator));
Log.Info(
"CombatScheduler started. Level={0}, PhaseCount={1}.",
_context.CurrentLevel.Id,
_context.PhaseLoopRuntime.PhaseCount);
return true;
}
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_context.CurrentState?.OnUpdate(elapseSeconds, realElapseSeconds);
}
public void OnDestroy()
{
if (!_initialized)
{
return;
}
_context.CurrentState?.OnExit();
_context.CurrentState?.OnDestroy();
_context.CurrentState = null;
_flowCoordinator.CleanupAllCombatEntities();
_flowCoordinator.CloseCombatFinishForm();
_flowCoordinator.CloseRewardSelectForm();
_flowCoordinator.CloseDialogForm();
_context.EnemyManager.OnDestroy();
_flowCoordinator.ResetRuntime();
_context.EventBridge.Unbind();
_context.CombatFinishFormUseCase = null;
_context.RewardSelectFormUseCase = null;
_context.Entity = null;
_initialized = false;
}
public bool TryEndCombatByPlayer()
{
if (_context.CurrentState is not CombatRunningPhaseState &&
_context.CurrentState is not CombatWaitingForPhaseEndState)
{
return false;
}
return _context.PhaseLoopRuntime.TryRequestEndCombat();
}
public void OnEnemyReachedBase(DREnemy enemy)
{
if (!IsRunning)
{
return;
}
int resolvedBaseDamage = enemy != null ? Mathf.Max(0, enemy.BaseDamage) : 0;
if (resolvedBaseDamage <= 0)
{
return;
}
_flowCoordinator.ApplyBaseDamage(resolvedBaseDamage);
}
public void OnEnemyDefeated(DREnemy enemy)
{
if (!IsRunning)
{
return;
}
EnemyDropResolveContext context = new(
enemy,
_context.PhaseLoopRuntime.DisplayPhaseIndex,
_flowCoordinator.ResolveCurrentThemeType());
EnemyDropResolveResult result = _context.EnemyDropResolver.Resolve(context);
_context.CombatInRunResourceManager.AddEnemyDefeatedReward(result.Coin, result.Gold);
if (!result.ShouldRollOutGameItem)
{
return;
}
_context.CombatInRunResourceManager.TryRollOutGameItemDrop(
context.DisplayPhaseIndex,
context.ThemeType);
}
public bool OnCombatFinishReturnRequested()
{
if (_context.CurrentState is not CombatWaitingForReturnState waitingForReturnState)
{
return false;
}
waitingForReturnState.RequestReturn();
return true;
}
public bool TryConsumeCoin(int coin)
{
return _context.CombatInRunResourceManager.TryConsumeCoin(coin);
}
public void AddCoin(int coin)
{
_context.CombatInRunResourceManager.AddCoin(coin);
}
public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)
{
return _context.CombatInRunResourceManager.TryGetBuildTowerStats(buildIndex, out stats);
}
public bool TryDebugFail(string errorMessage)
{
if (_context.IsCompleted || _context.CurrentState == null || _context.CurrentState is CombatFailedState)
{
return false;
}
_flowCoordinator.EnterFailureFallback(string.IsNullOrWhiteSpace(errorMessage)
? "Manual debug fail."
: errorMessage);
return _context.CurrentState is CombatFailedState;
}
void ICombatSchedulerHost.ChangeState(CombatStateBase nextState)
{
ChangeState(nextState);
}
internal void ChangeState(CombatStateBase nextState)
{
if (ReferenceEquals(_context.CurrentState, nextState))
{
return;
}
_context.CurrentState?.OnExit();
_context.CurrentState?.OnDestroy();
_context.CurrentState = nextState;
_context.CurrentState?.OnInit();
_context.CurrentState?.OnEnter();
}
#region Event Handlers
private void OnShowEntitySuccess(ShowEntitySuccessEventArgs args)
{
var status = _context.LoadSession.HandleShowEntitySuccess(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed)
{
_flowCoordinator.EnterFailureFallback(errorMessage);
return;
}
if (status == CombatLoadSession.EventHandleStatus.Succeeded)
{
MapEntity map = _context.LoadSession.CurrentMap;
Log.Info(
"Map ready. LevelId={0}, PathCells={1}, FoundationCells={2}, Spawners={3}, House={4}.",
_context.CurrentLevel != null ? _context.CurrentLevel.Id : 0,
map.PathCells.Count,
map.FoundationCells.Count,
map.Spawners.Length,
map.House != null ? map.House.name : "None");
}
}
private void OnShowEntityFailure(ShowEntityFailureEventArgs args)
{
var status = _context.LoadSession.HandleShowEntityFailure(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed)
{
_flowCoordinator.EnterFailureFallback(errorMessage);
}
}
private void OnHideEntityComplete(HideEntityCompleteEventArgs args)
{
_context.LoadSession.HandleHideEntityComplete(args);
}
private void OnOpenUIFormSuccess(OpenUIFormSuccessEventArgs args)
{
var status = _context.LoadSession.HandleOpenUIFormSuccess(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed)
{
_flowCoordinator.EnterFailureFallback(errorMessage);
}
}
private void OnOpenUIFormFailure(OpenUIFormFailureEventArgs args)
{
var status = _context.LoadSession.HandleOpenUIFormFailure(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed)
{
_flowCoordinator.EnterFailureFallback(errorMessage);
}
}
private void OnCloseUIFormComplete(CloseUIFormCompleteEventArgs args)
{
_context.LoadSession.HandleCloseUIFormComplete(args);
}
#endregion
}
}