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 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); } } }