323 lines
9.9 KiB
C#
323 lines
9.9 KiB
C#
using AibisDream.Framework;
|
|
using AibisDream.Kit;
|
|
using AibisDream.SaveSystem;
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using DG.Tweening;
|
|
|
|
namespace AibisDream.FixSystem
|
|
{
|
|
public enum SystemStartupMode
|
|
{
|
|
AutoOnHead,
|
|
Deferred
|
|
}
|
|
|
|
/// <summary>
|
|
/// 维修面板系统 - 统一管理所有侧边栏面板
|
|
/// 包括常驻面板(对话、任务)和随状态切换的游戏面板
|
|
/// </summary>
|
|
public class FixPanelSystem : Singleton<FixPanelSystem>
|
|
{
|
|
private void Start()
|
|
{
|
|
InitLight();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
RegisterCanvasCamera();
|
|
RegisterPanels();
|
|
RegisterScreenView();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
UnRegisterCanvasCamera();
|
|
_panelContainer.Clear();
|
|
UnRegisterScreenView();
|
|
}
|
|
|
|
#region 维修面板
|
|
|
|
private static IOCContainer _panelContainer = new();
|
|
|
|
public static T GetRepairPanel<T>() where T : class
|
|
{
|
|
return _panelContainer.Get<T>();
|
|
}
|
|
|
|
public void RegisterPanels()
|
|
{
|
|
var panels = GetComponentsInChildren<IOperatorPanel>(true);
|
|
foreach (var panel in panels)
|
|
{
|
|
_panelContainer.RegisterByInstance(panel);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 维修管理扩展
|
|
|
|
[Header("光照系统")]
|
|
[SerializeField] private bool isSystemOn = false;
|
|
public bool IsSystemOn => isSystemOn;
|
|
public bool ShouldAutoStartOnHeadEntry =>
|
|
!isSystemOn && (!isStartupPrepared || startupMode == SystemStartupMode.AutoOnHead);
|
|
|
|
[SerializeField] private FixPanelLight panelLight;
|
|
public FixPanelLight PanelLight => panelLight;
|
|
|
|
private bool isStarting;
|
|
private bool isStartupPrepared;
|
|
private SystemStartupMode startupMode = SystemStartupMode.AutoOnHead;
|
|
private LightState targetLightState = LightState.Normal;
|
|
|
|
[Header("维修切换管理")]
|
|
[SerializeField] private RepairSystemManager repairSystemManager;
|
|
public RepairSystemManager RepairSystemManager => repairSystemManager;
|
|
|
|
private void InitLight()
|
|
{
|
|
panelLight.Initialize(isSystemOn, targetLightState);
|
|
GetRepairPanel<CablePanel>().InitLight(isSystemOn);
|
|
// 无光环境下屏幕也不能亮
|
|
if (!isSystemOn)
|
|
{
|
|
screenPanel.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 系统启动协调
|
|
|
|
/// <summary>
|
|
/// 为指定剧情准备一次系统启动。只有显式调用该接口的 YarnProject 会改变默认表现。
|
|
/// </summary>
|
|
public void PrepareSystemStartup(SystemStartupMode mode)
|
|
{
|
|
startupMode = mode;
|
|
isStartupPrepared = true;
|
|
targetLightState = LightState.Normal;
|
|
ApplySystemOnImmediate(false);
|
|
taskPanel?.HidePanelImmediate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动所有系统(协调灯光系统和 CablePanel 的启动顺序)
|
|
/// </summary>
|
|
public IEnumerator StartAllSystems()
|
|
{
|
|
if (isSystemOn) yield break;
|
|
|
|
if (isStarting)
|
|
{
|
|
while (isStarting)
|
|
{
|
|
yield return null;
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
isStarting = true;
|
|
|
|
// 1. 先启动 CablePanel 的系统自检
|
|
var cablePanel = GetRepairPanel<CablePanel>();
|
|
if (cablePanel != null)
|
|
{
|
|
yield return cablePanel.StartSystem();
|
|
}
|
|
|
|
// 2. 再启动灯光序列
|
|
if (panelLight != null)
|
|
{
|
|
yield return panelLight.PanelLightStartSequence();
|
|
}
|
|
|
|
// 3. 再启动屏幕面板
|
|
screenPanel.gameObject.SetActive(true);
|
|
|
|
isSystemOn = true;
|
|
isStarting = false;
|
|
isStartupPrepared = false;
|
|
|
|
// 开机动画使用 Normal 表现,完成后再进入 Yarn 在关机期间缓存的目标状态。
|
|
ApplyLightState(targetLightState);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置灯光状态,并同步更新相关面板的灯光效果
|
|
/// </summary>
|
|
public void SetLightState(LightState newState)
|
|
{
|
|
targetLightState = newState;
|
|
|
|
// 关机时只缓存目标状态,避免序章的 Warning 提前点亮面板。
|
|
if (!isSystemOn) return;
|
|
|
|
ApplyLightState(newState);
|
|
}
|
|
|
|
private void ApplyLightState(LightState newState)
|
|
{
|
|
// 1. 更新主灯光系统
|
|
if (panelLight != null)
|
|
{
|
|
panelLight.SetLightState(newState);
|
|
}
|
|
|
|
// 2. 同步更新 CablePanel 的灯光效果
|
|
var cablePanel = GetRepairPanel<CablePanel>();
|
|
if (cablePanel != null)
|
|
{
|
|
cablePanel.UpdateBreathingLight(newState);
|
|
cablePanel.UpdateECGWave(newState);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 快照 Capture / Restore
|
|
|
|
public FixPanelSnapshotDto CapturePanelSnapshot()
|
|
{
|
|
var cablePanel = GetRepairPanel<CablePanel>();
|
|
var dto = new FixPanelSnapshotDto
|
|
{
|
|
isSystemOn = isSystemOn,
|
|
lightState = targetLightState.ToString(),
|
|
currentRepairSystemType = repairSystemManager?.GetCurrentSystemType()?.Name,
|
|
isCableRetracted = cablePanel != null && cablePanel.isCableRetracted
|
|
};
|
|
|
|
if (cablePanel != null && cablePanel.TryGetPluggedModuleName(out var moduleName))
|
|
{
|
|
dto.pluggedModuleName = moduleName;
|
|
}
|
|
|
|
if (dto.isCableRetracted && !string.IsNullOrEmpty(dto.pluggedModuleName))
|
|
{
|
|
Debug.LogWarning("[FixPanelSystem] Capture: isCableRetracted 与 pluggedModuleName 冲突,忽略 pluggedModuleName。");
|
|
dto.pluggedModuleName = null;
|
|
}
|
|
|
|
if (taskPanel != null)
|
|
{
|
|
taskPanel.CaptureSnapshot(out dto.isTaskPanelVisible, dto.tasks);
|
|
}
|
|
|
|
return dto;
|
|
}
|
|
|
|
public void ApplyPanelSnapshot(FixPanelSnapshotDto dto)
|
|
{
|
|
if (dto == null) return;
|
|
|
|
// 读档恢复的是稳定终态,不继承可能仍留在场景中的待启动模式。
|
|
isStartupPrepared = false;
|
|
startupMode = SystemStartupMode.AutoOnHead;
|
|
targetLightState = LightState.Normal;
|
|
if (!string.IsNullOrEmpty(dto.lightState)
|
|
&& !System.Enum.TryParse(dto.lightState, true, out targetLightState))
|
|
{
|
|
Debug.LogWarning($"[FixPanelSystem] Unknown saved light state '{dto.lightState}'; using Normal.");
|
|
targetLightState = LightState.Normal;
|
|
}
|
|
|
|
ApplySystemOnImmediate(dto.isSystemOn);
|
|
|
|
if (repairSystemManager != null)
|
|
{
|
|
repairSystemManager.RestoreSystemType(dto.currentRepairSystemType);
|
|
}
|
|
|
|
GetRepairPanel<CablePanel>()?.ApplyCableSnapshot(dto.isCableRetracted, dto.pluggedModuleName);
|
|
|
|
taskPanel?.ApplySnapshot(dto.isTaskPanelVisible, dto.tasks, immediate: true);
|
|
}
|
|
|
|
/// <summary>读档终态:跳过 StartAllSystems 动画,直接写面板壳层状态。</summary>
|
|
public void ApplySystemOnImmediate(bool isOn)
|
|
{
|
|
isSystemOn = isOn;
|
|
isStarting = false;
|
|
|
|
if (panelLight != null)
|
|
{
|
|
panelLight.Initialize(isOn, targetLightState);
|
|
}
|
|
|
|
var cablePanel = GetRepairPanel<CablePanel>();
|
|
cablePanel?.InitLight(isOn);
|
|
|
|
if (screenPanel != null)
|
|
{
|
|
screenPanel.gameObject.SetActive(isOn);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 屏幕相关操作
|
|
[Header("默认画布")]
|
|
[SerializeField] private Canvas canvas;
|
|
|
|
[Header("全景遮罩")]
|
|
[SerializeField] private SpriteRenderer mask;
|
|
|
|
[Header("任务面板")]
|
|
[SerializeField] private TaskPanel taskPanel;
|
|
public TaskPanel TaskPanel => taskPanel;
|
|
|
|
[Header("屏幕面板")]
|
|
[SerializeField] private ScreenPanel screenPanel;
|
|
public ScreenPanel ScreenPanel => screenPanel;
|
|
|
|
private void RegisterScreenView()
|
|
{
|
|
DialogUIManager.Instance.RegisterScreenView(DialogViewType.Screen, screenPanel);
|
|
DialogUIManager.Instance.RegisterScreenView(DialogViewType.Task, taskPanel);
|
|
}
|
|
|
|
private void UnRegisterScreenView()
|
|
{
|
|
DialogUIManager.Instance.UnRegisterScreenView(DialogViewType.Screen);
|
|
DialogUIManager.Instance.UnRegisterScreenView(DialogViewType.Task);
|
|
}
|
|
|
|
private void RegisterCanvasCamera()
|
|
{
|
|
canvas.worldCamera = Camera.main;
|
|
}
|
|
|
|
private void UnRegisterCanvasCamera()
|
|
{
|
|
canvas.worldCamera = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 遮罩淡入(从当前透明度过渡到不透明)
|
|
/// </summary>
|
|
/// <param name="duration">淡入时长(秒)</param>
|
|
public IEnumerator MaskFadeIn(float duration = 1f)
|
|
{
|
|
if (mask == null) yield break;
|
|
yield return mask.DOFade(1f, duration).WaitForCompletion();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 遮罩淡出(从当前透明度过渡到透明)
|
|
/// </summary>
|
|
/// <param name="duration">淡出时长(秒)</param>
|
|
public IEnumerator MaskFadeOut(float duration = 1f)
|
|
{
|
|
if (mask == null) yield break;
|
|
yield return mask.DOFade(0f, duration).WaitForCompletion();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|