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

327 lines
12 KiB
C#

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