geometry-tower-defense/Assets/GameMain/Scripts/Procedure/ProcedureMain.cs

350 lines
13 KiB
C#

using GameFramework.Event;
using GameFramework.Fsm;
using GameFramework.Procedure;
using GeometryTD.CustomEvent;
using GeometryTD.Definition;
using GeometryTD.UI;
using UnityGameFramework.Runtime;
namespace GeometryTD.Procedure
{
public enum ProcedureMainFlowPhase
{
Hub = 0,
NodeActive = 1,
RunCompletedPendingFinish = 2
}
public enum ProcedureMainRunAdvanceResult
{
NoChange = 0,
NodeFailed = 1,
AdvancedToNextNode = 2,
RunCompleted = 3
}
public static class ProcedureMainRunFlowService
{
public static ProcedureMainRunAdvanceResult TryAdvanceRun(
RunState runState,
bool succeeded,
BackpackInventoryData snapshot)
{
if (!RunStateAdvanceService.TryCompleteCurrentNode(runState, succeeded, snapshot))
{
return ProcedureMainRunAdvanceResult.NoChange;
}
if (runState != null && runState.IsCompleted)
{
return ProcedureMainRunAdvanceResult.RunCompleted;
}
return succeeded
? ProcedureMainRunAdvanceResult.AdvancedToNextNode
: ProcedureMainRunAdvanceResult.NodeFailed;
}
}
public class ProcedureMain : ProcedureBase
{
public override bool UseNativeDialog => false;
private RepoFormUseCase _repoFormUseCase;
private NodeMapFormUseCase _nodeMapFormUseCase;
private RunState _currentRunState;
private ProcedureMainFlowPhase _flowPhase = ProcedureMainFlowPhase.Hub;
#region FSM
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
GameEntry.Event.Subscribe(CombatFailureReturnEventArgs.EventId, OnCombatFailureReturn);
GameEntry.Event.Subscribe(NodeCompleteEventArgs.EventId, OnNodeComplete);
GameEntry.Event.Subscribe(NodeEnterEventArgs.EventId, OnNodeEnter);
GameEntry.Event.Subscribe(NodeMapNodeEnterRequestedEventArgs.EventId, OnNodeMapNodeEnterRequested);
GameEntry.EventNode.OnInit();
GameEntry.CombatNode.OnInit(LevelThemeType.Plain);
GameEntry.ShopNode.OnInit();
GameEntry.PlayerInventory?.OnInit();
_currentRunState = RunStateFactory.CreateFixedRun(
LevelThemeType.Plain,
GameEntry.PlayerInventory != null
? GameEntry.PlayerInventory.GetInventorySnapshot()
: null);
_repoFormUseCase = new RepoFormUseCase();
GameEntry.UIRouter.BindUIUseCase(UIFormType.RepoForm, _repoFormUseCase);
_nodeMapFormUseCase = new NodeMapFormUseCase();
_nodeMapFormUseCase.SetRunState(_currentRunState);
GameEntry.UIRouter.BindUIUseCase(UIFormType.NodeMapForm, _nodeMapFormUseCase);
EnterHubFlow();
}
protected override void OnUpdate(IFsm<IProcedureManager> procedureOwner, float elapseSeconds,
float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
GameEntry.CombatNode.OnUpdate(elapseSeconds, realElapseSeconds);
}
protected override void OnLeave(IFsm<IProcedureManager> procedureOwner, bool isShutdown)
{
GameEntry.CombatNode.OnShutdown();
GameEntry.Event.Unsubscribe(NodeMapNodeEnterRequestedEventArgs.EventId, OnNodeMapNodeEnterRequested);
GameEntry.Event.Unsubscribe(CombatFailureReturnEventArgs.EventId, OnCombatFailureReturn);
GameEntry.Event.Unsubscribe(NodeEnterEventArgs.EventId, OnNodeEnter);
GameEntry.Event.Unsubscribe(NodeCompleteEventArgs.EventId, OnNodeComplete);
GameEntry.UIRouter.CloseUI(UIFormType.RepoForm);
GameEntry.UIRouter.CloseUI(UIFormType.NodeMapForm);
GameEntry.UIRouter.CloseUI(UIFormType.MainForm);
_repoFormUseCase = null;
_nodeMapFormUseCase = null;
_currentRunState = null;
_flowPhase = ProcedureMainFlowPhase.Hub;
base.OnLeave(procedureOwner, isShutdown);
}
#endregion
private void OnNodeEnter(object sender, GameEventArgs e)
{
if (!(e is NodeEnterEventArgs args))
{
return;
}
if (!string.IsNullOrWhiteSpace(args.RunId) &&
_currentRunState != null &&
!string.Equals(args.RunId, _currentRunState.RunId))
{
Log.Warning(
"ProcedureMain.OnNodeEnter() ignored. EventRunId={0}, CurrentRunId={1}.",
args.RunId,
_currentRunState.RunId);
return;
}
RunNodeState currentNode = _currentRunState?.CurrentNode;
if (currentNode != null &&
((args.NodeId > 0 && args.NodeId != currentNode.NodeId) ||
(args.NodeType != RunNodeType.None && args.NodeType != currentNode.NodeType) ||
(args.SequenceIndex >= 0 && args.SequenceIndex != currentNode.SequenceIndex)))
{
Log.Warning(
"ProcedureMain.OnNodeEnter() node mismatch. EventNodeId={0}, EventNodeType={1}, EventSequenceIndex={2}; CurrentNodeId={3}, CurrentNodeType={4}, CurrentSequenceIndex={5}.",
args.NodeId,
args.NodeType,
args.SequenceIndex,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
}
Log.Info(
"ProcedureMain.OnNodeEnter() accepted. RunId={0}, NodeId={1}, NodeType={2}, SequenceIndex={3}.",
string.IsNullOrWhiteSpace(args.RunId) ? _currentRunState?.RunId : args.RunId,
args.NodeId,
args.NodeType,
args.SequenceIndex);
EnterNodeFlow();
}
private void OnNodeComplete(object sender, GameEventArgs e)
{
if (!(e is NodeCompleteEventArgs args))
{
return;
}
if (!string.IsNullOrWhiteSpace(args.RunId) &&
_currentRunState != null &&
!string.Equals(args.RunId, _currentRunState.RunId))
{
Log.Warning(
"ProcedureMain.OnNodeComplete() ignored. EventRunId={0}, CurrentRunId={1}.",
args.RunId,
_currentRunState.RunId);
return;
}
BackpackInventoryData snapshot = args.InventorySnapshotAfterNode;
if (snapshot == null && GameEntry.PlayerInventory != null)
{
snapshot = GameEntry.PlayerInventory.GetInventorySnapshot();
}
HandleRunAdvanceResult(ProcedureMainRunFlowService.TryAdvanceRun(_currentRunState, args.Succeeded, snapshot));
}
private void OnCombatFailureReturn(object sender, GameEventArgs e)
{
if (!(e is CombatFailureReturnEventArgs))
{
return;
}
BackpackInventoryData snapshot = GameEntry.PlayerInventory != null
? GameEntry.PlayerInventory.GetInventorySnapshot()
: null;
HandleRunAdvanceResult(ProcedureMainRunFlowService.TryAdvanceRun(_currentRunState, false, snapshot));
}
private void OnNodeMapNodeEnterRequested(object sender, GameEventArgs e)
{
if (!(sender is NodeMapForm) || !(e is NodeMapNodeEnterRequestedEventArgs args))
{
return;
}
RunNodeState currentNode = _currentRunState?.CurrentNode;
if (_currentRunState == null ||
_flowPhase != ProcedureMainFlowPhase.Hub ||
!_currentRunState.CanEnterCurrentNode ||
currentNode == null)
{
Log.Warning(
"ProcedureMain.OnNodeMapNodeEnterRequested() ignored. FlowPhase={0}, HasEnterableNode={1}.",
_flowPhase,
_currentRunState != null && _currentRunState.CanEnterCurrentNode);
return;
}
if (args.SequenceIndex != currentNode.SequenceIndex || args.NodeType != currentNode.NodeType)
{
Log.Warning(
"ProcedureMain.OnNodeMapNodeEnterRequested() ignored. Requested={0}#{1}, CurrentNode={2}#{3}.",
args.NodeType,
args.SequenceIndex,
currentNode.NodeType,
currentNode.SequenceIndex);
return;
}
switch (currentNode.NodeType)
{
case RunNodeType.Combat:
case RunNodeType.BossCombat:
if (!HasAvailableParticipantTower())
{
Log.Warning("ProcedureMain blocked combat start. No participant tower is available.");
return;
}
GameEntry.CombatNode.StartCombat(
currentNode.LinkedLevelId,
_currentRunState.RunId,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
return;
case RunNodeType.Event:
GameEntry.EventNode.StartEvent(
_currentRunState.RunId,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
return;
case RunNodeType.Shop:
GameEntry.ShopNode.StartShop(
_currentRunState.RunId,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
return;
default:
Log.Warning("ProcedureMain.OnNodeMapNodeEnterRequested() encountered unsupported node type: {0}.",
currentNode.NodeType);
return;
}
}
private void HandleRunAdvanceResult(ProcedureMainRunAdvanceResult result)
{
switch (result)
{
case ProcedureMainRunAdvanceResult.NoChange:
return;
case ProcedureMainRunAdvanceResult.NodeFailed:
Log.Info(
"ProcedureMain current node failed. RunId={0}, NodeId={1}, NodeType={2}, SequenceIndex={3}.",
_currentRunState?.RunId,
_currentRunState?.CurrentNode?.NodeId ?? 0,
_currentRunState?.CurrentNode?.NodeType ?? RunNodeType.None,
_currentRunState?.CurrentNode?.SequenceIndex ?? -1);
EnterHubFlow();
return;
case ProcedureMainRunAdvanceResult.AdvancedToNextNode:
LogNextNode();
EnterHubFlow();
return;
case ProcedureMainRunAdvanceResult.RunCompleted:
EnterRunCompletedPendingFinish();
return;
default:
return;
}
}
private static bool HasAvailableParticipantTower()
{
return GameEntry.PlayerInventory != null &&
GameEntry.PlayerInventory.GetParticipantTowerSnapshot().Count > 0;
}
private void EnterHubFlow()
{
_flowPhase = ProcedureMainFlowPhase.Hub;
_nodeMapFormUseCase?.SetRunState(_currentRunState);
GameEntry.UIRouter.OpenUI(UIFormType.NodeMapForm);
GameEntry.UIRouter.OpenUI(UIFormType.MainForm);
}
private void EnterNodeFlow()
{
_flowPhase = ProcedureMainFlowPhase.NodeActive;
CloseHubUI();
}
private void EnterRunCompletedPendingFinish()
{
_flowPhase = ProcedureMainFlowPhase.RunCompletedPendingFinish;
_nodeMapFormUseCase?.SetRunState(_currentRunState);
CloseHubUI();
Log.Info("ProcedureMain run completed. RunId={0}", _currentRunState?.RunId);
}
private void CloseHubUI()
{
GameEntry.UIRouter.CloseUI(UIFormType.NodeMapForm);
GameEntry.UIRouter.CloseUI(UIFormType.MainForm);
}
private void LogNextNode()
{
RunNodeState nextNode = _currentRunState?.CurrentNode;
if (nextNode == null)
{
return;
}
Log.Info(
"ProcedureMain advanced run. RunId={0}, NextNodeType={1}, SequenceIndex={2}, LevelId={3}.",
_currentRunState.RunId,
nextNode.NodeType,
nextNode.SequenceIndex,
nextNode.LinkedLevelId);
}
}
}