using System;
using UnityEngine;
namespace Cielonos.MainGame
{
public partial class RunManager
{
///
/// 管理地图节点选择、进入和完成。
/// 这里负责把 RunMapNode 转换为 RunPhase 与 Zone 加载请求,但不直接处理 CombatRoom 的敌人死亡事件。
///
public class RunNodeFlowSubmodule : SubmoduleBase
{
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);
}
}
}
}