283 lines
9.5 KiB
C#
283 lines
9.5 KiB
C#
using System.Collections.Generic;
|
|
using GeometryTD.CustomEvent;
|
|
using GeometryTD.DataTable;
|
|
using GeometryTD.Definition;
|
|
using GeometryTD.Procedure;
|
|
using GeometryTD.UI;
|
|
using UnityEngine;
|
|
using UnityGameFramework.Runtime;
|
|
|
|
namespace GeometryTD.CustomComponent
|
|
{
|
|
internal sealed class CombatSchedulerCoordinator
|
|
{
|
|
private readonly ICombatSchedulerPort _schedulerPort;
|
|
private readonly CombatSchedulerRuntime _runtime;
|
|
public ICombatSchedulerPort Port => _schedulerPort;
|
|
|
|
public CombatSchedulerCoordinator(ICombatSchedulerPort schedulerPort, CombatSchedulerRuntime runtime)
|
|
{
|
|
_schedulerPort = schedulerPort;
|
|
_runtime = runtime;
|
|
}
|
|
|
|
public void ChangeState(CombatStateBase nextState)
|
|
{
|
|
_schedulerPort.ChangeState(nextState);
|
|
}
|
|
|
|
public int ResolveEnemyHpRateMultiplier(int displayPhaseIndex, int phaseCount)
|
|
{
|
|
if (displayPhaseIndex <= 0 || phaseCount <= 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int completedLoopCount = Mathf.Max(0, (displayPhaseIndex - 1) / phaseCount);
|
|
if (completedLoopCount >= 30)
|
|
{
|
|
return int.MaxValue;
|
|
}
|
|
|
|
return 1 << completedLoopCount;
|
|
}
|
|
|
|
public void ResetRuntime()
|
|
{
|
|
_runtime.CurrentState = null;
|
|
_runtime.PhaseBuffer.Clear();
|
|
_runtime.SpawnEntriesByPhaseId.Clear();
|
|
_runtime.PhaseLoopRuntime.Reset();
|
|
_runtime.LoadSession.Reset();
|
|
_runtime.CombatRunResourceStore.Reset();
|
|
_runtime.SettlementContext = null;
|
|
_runtime.CurrentLevel = null;
|
|
_runtime.DidCombatWin = true;
|
|
_runtime.IsCompleted = false;
|
|
_runtime.NodeEnterFired = false;
|
|
_runtime.NodeContext = null;
|
|
_runtime.NextDropOrdinal = 0;
|
|
_runtime.NextRewardOrdinal = 0;
|
|
}
|
|
|
|
public void CleanupAllCombatEntities()
|
|
{
|
|
_runtime.LoadSession.Cleanup();
|
|
_runtime.EnemyManager.CleanupTrackedEnemies();
|
|
}
|
|
|
|
public void EnsureCombatFinishFormUseCaseBound()
|
|
{
|
|
_runtime.CombatFinishFormUseCase ??= new CombatFinishFormUseCase(_schedulerPort);
|
|
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatFinishForm, _runtime.CombatFinishFormUseCase);
|
|
}
|
|
|
|
public void EnsureRewardSelectFormUseCaseBound()
|
|
{
|
|
_runtime.RewardSelectFormUseCase ??= new RewardSelectFormUseCase();
|
|
GameEntry.UIRouter.BindUIUseCase(UIFormType.RewardSelectForm, _runtime.RewardSelectFormUseCase);
|
|
}
|
|
|
|
public void OpenCombatFailureDialog(string errorMessage)
|
|
{
|
|
CloseDialogForm();
|
|
GameEntry.UIRouter.OpenUI(UIFormType.DialogForm, new DialogFormRawData
|
|
{
|
|
Mode = 1,
|
|
Title = "Combat Error",
|
|
Message = string.IsNullOrWhiteSpace(errorMessage) ? "Combat failed unexpectedly." : errorMessage,
|
|
PauseGame = false,
|
|
ConfirmText = "Return Menu",
|
|
OnClickConfirm = OnCombatFailureDialogConfirmed
|
|
});
|
|
}
|
|
|
|
public bool TryBeginNextPhase()
|
|
{
|
|
if (!_runtime.PhaseLoopRuntime.TryEnterNextPhase(out DRLevelPhase nextPhase))
|
|
{
|
|
_schedulerPort.ChangeState(new CombatSettlementState(_runtime, this, true));
|
|
return false;
|
|
}
|
|
|
|
_runtime.SpawnEntriesByPhaseId.TryGetValue(nextPhase.Id, out IReadOnlyList<DRLevelSpawnEntry> spawnEntries);
|
|
_schedulerPort.ChangeState(new CombatRunningPhaseState(_runtime, this, nextPhase, spawnEntries));
|
|
return true;
|
|
}
|
|
|
|
public void EnterWaitingForPhaseEnd()
|
|
{
|
|
_schedulerPort.ChangeState(new CombatWaitingForPhaseEndState(_runtime, this));
|
|
}
|
|
|
|
public void CompleteCurrentPhase()
|
|
{
|
|
_runtime.EnemyManager.EndPhase();
|
|
Log.Info(
|
|
"CombatScheduler phase completed. Level={0}, Phase={1}, Elapsed={2:F2}s.",
|
|
_runtime.CurrentLevel != null ? _runtime.CurrentLevel.Id : 0,
|
|
_runtime.PhaseLoopRuntime.CurrentPhase != null ? _runtime.PhaseLoopRuntime.CurrentPhase.Id : 0,
|
|
_runtime.PhaseLoopRuntime.CurrentPhaseElapsed);
|
|
|
|
TryBeginNextPhase();
|
|
}
|
|
|
|
public bool ShouldEnterSettlementFromActiveState(out bool didCombatWin)
|
|
{
|
|
if (GetCurrentBaseHp() <= 0)
|
|
{
|
|
didCombatWin = false;
|
|
return true;
|
|
}
|
|
|
|
if (_runtime.PhaseLoopRuntime.IsEndCombatRequested)
|
|
{
|
|
didCombatWin = true;
|
|
return true;
|
|
}
|
|
|
|
didCombatWin = true;
|
|
return false;
|
|
}
|
|
|
|
public void OnFullBaseHpRewardSelected(RewardSelectItemRawData selectedReward)
|
|
{
|
|
if (_runtime.CurrentState is not CombatRewardSelectionState || _runtime.SettlementContext == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_runtime.CombatSettlementService.ApplySelectedReward(_runtime.SettlementContext, selectedReward);
|
|
_schedulerPort.ChangeState(new CombatFinishFormState(_runtime, this));
|
|
}
|
|
|
|
public void OnFullBaseHpRewardGiveUp()
|
|
{
|
|
if (_runtime.CurrentState is not CombatRewardSelectionState || _runtime.SettlementContext == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_schedulerPort.ChangeState(new CombatFinishFormState(_runtime, this));
|
|
}
|
|
|
|
public LevelThemeType ResolveCurrentThemeType()
|
|
{
|
|
if (_runtime.CurrentLevel != null)
|
|
{
|
|
return _runtime.CurrentLevel.LevelThemeType;
|
|
}
|
|
|
|
return LevelThemeType.None;
|
|
}
|
|
|
|
public int ApplyBaseDamage(int damage)
|
|
{
|
|
return _runtime.CombatRunResourceStore.ApplyBaseDamage(damage);
|
|
}
|
|
|
|
public int GetCurrentBaseHp()
|
|
{
|
|
return Mathf.Max(0, _runtime.CombatRunResourceStore.CurrentBaseHp);
|
|
}
|
|
|
|
public void CloseCombatFinishForm()
|
|
{
|
|
GameEntry.UIRouter.CloseUI(UIFormType.CombatFinishForm);
|
|
}
|
|
|
|
public void CloseRewardSelectForm()
|
|
{
|
|
GameEntry.UIRouter.CloseUI(UIFormType.RewardSelectForm);
|
|
}
|
|
|
|
public void CloseDialogForm()
|
|
{
|
|
GameEntry.UIRouter.CloseUI(UIFormType.DialogForm);
|
|
}
|
|
|
|
public void CompleteNormalCombatAndNotify(bool didCombatWin)
|
|
{
|
|
CompleteCombat(didCombatWin);
|
|
RunNodeExecutionContext nodeContext = _runtime.NodeContext;
|
|
GameEntry.Event.Fire(
|
|
this,
|
|
NodeCompleteEventArgs.Create(
|
|
nodeContext?.RunId,
|
|
nodeContext?.NodeId ?? 0,
|
|
nodeContext?.NodeType ?? RunNodeType.None,
|
|
nodeContext?.SequenceIndex ?? -1,
|
|
RunNodeCompletionStatus.Completed,
|
|
didCombatWin,
|
|
nodeContext != null
|
|
? nodeContext.CreateCompletionSnapshot(
|
|
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
|
|
: null));
|
|
}
|
|
|
|
public void CompleteFailureCombatAndNotify()
|
|
{
|
|
CleanupAllCombatEntities();
|
|
CloseCombatFinishForm();
|
|
CloseRewardSelectForm();
|
|
CloseDialogForm();
|
|
CompleteCombat(false);
|
|
RunNodeExecutionContext nodeContext = _runtime.NodeContext;
|
|
GameEntry.Event.Fire(
|
|
this,
|
|
NodeCompleteEventArgs.Create(
|
|
nodeContext?.RunId,
|
|
nodeContext?.NodeId ?? 0,
|
|
nodeContext?.NodeType ?? RunNodeType.None,
|
|
nodeContext?.SequenceIndex ?? -1,
|
|
RunNodeCompletionStatus.Exception,
|
|
false,
|
|
nodeContext != null
|
|
? nodeContext.CreateCompletionSnapshot(
|
|
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
|
|
: null));
|
|
}
|
|
|
|
public bool HandleStartFailure(string errorMessage)
|
|
{
|
|
Log.Warning("{0}", errorMessage);
|
|
_runtime.EnemyManager.EndPhase();
|
|
CleanupAllCombatEntities();
|
|
CloseCombatFinishForm();
|
|
CloseRewardSelectForm();
|
|
CloseDialogForm();
|
|
ResetRuntime();
|
|
return false;
|
|
}
|
|
|
|
public void EnterFailureFallback(string errorMessage)
|
|
{
|
|
if (_runtime.CurrentState is CombatFailedState || _runtime.IsCompleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_schedulerPort.ChangeState(new CombatFailedState(_runtime, this, errorMessage));
|
|
}
|
|
|
|
public void OnCombatFailureDialogConfirmed(object userData)
|
|
{
|
|
_ = userData;
|
|
if (_runtime.CurrentState is not CombatFailedState || _runtime.IsCompleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CompleteFailureCombatAndNotify();
|
|
}
|
|
|
|
private void CompleteCombat(bool succeeded)
|
|
{
|
|
_runtime.IsCompleted = true;
|
|
_runtime.CurrentState = null;
|
|
_runtime.CombatRunResourceStore.MarkCombatEnded();
|
|
_runtime.CombatEndedCallback?.Invoke(succeeded);
|
|
}
|
|
}
|
|
}
|