Files
Cielonos/Assets/Scripts/MainGame/GameRun/RunManager/RunManager_NodeFlowSubmodule.cs
SoulliesOfficial 39b43680a9 爆更
2026-07-18 03:16:20 -04:00

183 lines
7.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}
}