183 lines
7.0 KiB
C#
183 lines
7.0 KiB
C#
using System;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame
|
||
{
|
||
public partial class RunManager
|
||
{
|
||
/// <summary>
|
||
/// 管理地图节点选择、进入和完成。
|
||
/// 这里负责把 RunMapNode 转换为 RunPhase 与 Zone 加载请求,但不直接处理 CombatRoom 的敌人死亡事件。
|
||
/// </summary>
|
||
public class RunNodeFlowSubmodule : SubmoduleBase<RunManager>
|
||
{
|
||
public RunNodeFlowSubmodule(RunManager owner) : base(owner)
|
||
{
|
||
}
|
||
|
||
public void SelectNode(Vector2Int targetPosition)
|
||
{
|
||
if (owner.currentRun == null)
|
||
{
|
||
Debug.LogWarning("[RunManager] 当前没有进行中的 Run,忽略节点选择。");
|
||
return;
|
||
}
|
||
|
||
if (!owner.CanSelectMapNodes)
|
||
{
|
||
LogMapSelectionBlocked();
|
||
return;
|
||
}
|
||
|
||
if (targetPosition == owner.currentRun.currentPosition)
|
||
{
|
||
Debug.Log("[RunManager] 目标即当前位置,忽略。");
|
||
return;
|
||
}
|
||
|
||
if (!IsNodeReachable(targetPosition))
|
||
{
|
||
Debug.LogWarning(
|
||
$"[RunManager] 节点 {targetPosition} 不可到达" +
|
||
"(不在已访问节点的1格范围内)。");
|
||
return;
|
||
}
|
||
|
||
if (!owner.currentRun.mapData.nodes.TryGetValue(targetPosition, out RunMapNode targetNode))
|
||
{
|
||
Debug.LogError($"[RunManager] 节点 {targetPosition} 在地图数据中不存在。");
|
||
return;
|
||
}
|
||
|
||
owner.currentRun.currentPosition = targetPosition;
|
||
bool isFirstVisit = owner.currentRun.visitedNodes.Add(targetPosition);
|
||
bool isCompleted = owner.currentRun.completedNodes.Contains(targetPosition);
|
||
|
||
owner.RaiseNodeSelected(targetPosition);
|
||
owner.RaiseZoneEntered(new ZoneEntryContext(targetPosition, targetNode, isFirstVisit, isCompleted));
|
||
Debug.Log($"[RunManager] 移动到节点 {targetPosition},类型:{targetNode.nodeType}" +
|
||
$",首次进入:{isFirstVisit},已完成:{isCompleted}");
|
||
|
||
CombatManager.AttackAreaSm.DestroyAll();
|
||
EnterNode(targetNode);
|
||
}
|
||
|
||
public void CompleteCurrentNode()
|
||
{
|
||
if (owner.currentRun == null) return;
|
||
|
||
Vector2Int pos = owner.currentRun.currentPosition;
|
||
if (owner.currentRun.completedNodes.Contains(pos))
|
||
{
|
||
Debug.Log($"[RunManager] 节点 {pos} 已完成过,跳过重复结算。但检查是否需要切回 MapSelection。");
|
||
if (owner.currentPhase != RunPhase.MapSelection)
|
||
{
|
||
owner.phaseSm.TransitionToPhase(RunPhase.MapSelection);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
if (!owner.currentRun.mapData.nodes.TryGetValue(pos, out RunMapNode node))
|
||
{
|
||
return;
|
||
}
|
||
|
||
owner.currentRun.completedNodes.Add(pos);
|
||
|
||
if (RunNodeTypeUtility.ShouldCountAsClearedRoom(node.nodeType))
|
||
{
|
||
owner.currentRun.roomsCleared++;
|
||
}
|
||
|
||
if (RunNodeTypeUtility.IsSingleUseNode(node.nodeType))
|
||
{
|
||
owner.currentRun.exhaustedNodes.Add(pos);
|
||
}
|
||
|
||
owner.RaiseNodeCompleted(node);
|
||
|
||
if (owner.currentRun.currentPosition == owner.currentRun.mapData.bossPosition)
|
||
{
|
||
owner.currentRun.isCompleted = true;
|
||
Debug.Log("[RunManager] Boss 已击败,Run 通关!");
|
||
owner.phaseSm.TransitionToPhase(RunPhase.Settlement);
|
||
owner.RaiseRunEnded(owner.currentRun);
|
||
return;
|
||
}
|
||
|
||
owner.phaseSm.TransitionToPhase(RunPhase.MapSelection);
|
||
}
|
||
|
||
private bool IsNodeReachable(Vector2Int targetPosition)
|
||
{
|
||
if (owner.currentRun.visitedNodes.Contains(targetPosition))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
foreach (Vector2Int visitedPos in owner.currentRun.visitedNodes)
|
||
{
|
||
if (!owner.currentRun.mapData.nodes.TryGetValue(visitedPos, out RunMapNode node))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (node.connectedPositions.Contains(targetPosition))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private void LogMapSelectionBlocked()
|
||
{
|
||
if (owner.currentRun == null)
|
||
{
|
||
Debug.LogWarning("[RunManager] 当前没有进行中的 Run,忽略节点选择。");
|
||
return;
|
||
}
|
||
|
||
if (!owner.phaseSm.IsPhaseAllowingNodeSelection())
|
||
{
|
||
Debug.LogWarning($"[RunManager] 当前阶段为 {owner.currentPhase},不允许选择节点。");
|
||
return;
|
||
}
|
||
|
||
if (owner.HasPendingCombatRewards)
|
||
{
|
||
Debug.LogWarning($"[RunManager] 当前仍有 {owner.combatRewardSm.PendingCount} 个 Combat 奖励未领取,不能前往下一个节点。");
|
||
}
|
||
}
|
||
|
||
private void EnterNode(RunMapNode node)
|
||
{
|
||
bool isCompleted = owner.currentRun.completedNodes.Contains(owner.currentRun.currentPosition);
|
||
RunPhase targetPhase = isCompleted ? RunPhase.MapSelection : RunNodeTypeUtility.ToRunPhase(node.nodeType);
|
||
bool shouldStartBattleSetup = RunNodeTypeUtility.ShouldStartBattleSetup(node, isCompleted);
|
||
ZoneSetupMode setupMode = shouldStartBattleSetup
|
||
? ZoneSetupMode.CombatEncounter
|
||
: ZoneSetupMode.EnvironmentOnly;
|
||
|
||
if (isCompleted)
|
||
{
|
||
Debug.Log($"[RunManager] 节点 {owner.currentRun.currentPosition} 已完成,跳过 {RunNodeTypeUtility.ToRunPhase(node.nodeType)},直接进入 MapSelection。");
|
||
}
|
||
|
||
if (node.zoneData == null)
|
||
{
|
||
throw new Exception($"[RunManager] 节点 {owner.currentRun.currentPosition} 没有配置 ZoneData,无法进入对应阶段。");
|
||
}
|
||
|
||
owner.phaseSm.TransitionToPhase(RunPhase.Transitioning);
|
||
MapManager.Instance.LoadZone(node.zoneData, onComplete: () =>
|
||
{
|
||
owner.phaseSm.TransitionToPhase(targetPhase);
|
||
}, setupMode: setupMode);
|
||
}
|
||
}
|
||
}
|
||
}
|