Files
aibis-dream/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleSystem.cs
T

492 lines
16 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using UnityEngine;
using AibisDream.FixSystem;
using AibisDream.Kit;
using AibisDream.SaveSystem;
namespace AibisDream.FixSystem
{
public class BodyModuleSystem : MonoBehaviour
{
#region 内部索引
public List<BodyModule> BodyModules { get; private set; }
private GameObject _bodyModuleGroup;
private GameObject _headModuleGroup;
[HideInInspector] public HeatMapController heatMapController;
#endregion
#region 系统状态
private ModuleState _moduleState = ModuleState.Body;
public bool inHotErr;
private int _heatValue;
/// <summary>获取当前 BodyModule 分组(Body/Head)。</summary>
public ModuleState GetCurrentModuleState() => _moduleState;
private int HeatValue
{
get => _heatValue;
set
{
_heatValue = value;
inHotErr = _heatValue > 0;
}
}
#endregion
[SerializeField] public float plugSnappingRange = 1f;
private Vector3 _headModuleOriginalPos;
private Coroutine _breathingCoroutine;
private Coroutine _painShakeCoroutine;
private Coroutine _continuousPainCoroutine;
private void Awake()
{
InitComponentRefs();
}
private void InitComponentRefs()
{
// 处理内部索引
_bodyModuleGroup = transform.Find("Scalehandler")?.transform.Find("Body Module Group")?.gameObject;
_headModuleGroup = transform.Find("Scalehandler")?.transform.Find("Head Module Group")?.gameObject;
if (transform.Find("HeatMapController"))
{
heatMapController = transform.Find("HeatMapController").GetComponent<HeatMapController>();
}
FixSystemCenter.SystemDic.Register(this);
}
/// <summary>捕获插线模块持久状态(分组 + UF + Chip + 高亮/报警)。</summary>
public BodyModuleSnapshotDto CaptureBodyModuleSnapshot()
{
var dto = new BodyModuleSnapshotDto
{
moduleState = _moduleState.ToString()
};
var ufModule = FindBodyModuleByName("UF")?.GetComponent<UFModule>();
ufModule?.CaptureState(dto);
var chipModule = FindBodyModuleByName("Color")?.GetComponent<ChipModule>();
chipModule?.CaptureState(dto);
foreach (var module in EnumerateAllBodyModules())
{
var name = module.data.moduleName;
if (module.IsHighlighted) dto.highlightedModules.Add(name);
if (module.IsAlarmed) dto.alarmedModules.Add(name);
if (module.SupportsConditionVisual)
dto.moduleConditions[name] = module.Condition.ToString();
}
return dto;
}
/// <summary>写回插线模块持久状态;须在 fix cue 还原之后调用。</summary>
public void ApplyBodyModuleSnapshot(BodyModuleSnapshotDto dto)
{
if (dto == null) return;
if (!string.IsNullOrEmpty(dto.moduleState)
&& Enum.TryParse<ModuleState>(dto.moduleState, out var moduleState))
{
SwitchModuleGroup(moduleState);
}
var ufModule = FindBodyModuleByName("UF")?.GetComponent<UFModule>();
ufModule?.ApplyState(dto);
var chipModule = FindBodyModuleByName("Color")?.GetComponent<ChipModule>();
chipModule?.ApplyState(dto);
var highlighted = new HashSet<string>(dto.highlightedModules ?? Enumerable.Empty<string>());
var alarmed = new HashSet<string>(dto.alarmedModules ?? Enumerable.Empty<string>());
var moduleConditions = dto.moduleConditions ?? new Dictionary<string, string>();
foreach (var module in EnumerateAllBodyModules())
{
var name = module.data.moduleName;
module.Highlight(highlighted.Contains(name));
module.Alarm(alarmed.Contains(name));
if (!module.SupportsConditionVisual)
continue;
var condition = ModuleCondition.Normal;
if (moduleConditions.TryGetValue(name, out var conditionName)
&& !Enum.TryParse(conditionName, true, out condition))
{
Debug.LogWarning($"[BodyModuleSystem] Unknown saved module condition '{conditionName}' for {name}; using Normal.");
condition = ModuleCondition.Normal;
}
module.SetCondition(condition);
}
}
private IEnumerable<BodyModule> EnumerateAllBodyModules()
{
if (_bodyModuleGroup != null)
{
foreach (var module in _bodyModuleGroup.GetComponentsInChildren<BodyModule>(true))
yield return module;
}
if (_headModuleGroup != null)
{
foreach (var module in _headModuleGroup.GetComponentsInChildren<BodyModule>(true))
yield return module;
}
}
/// <summary>
/// 寻找最接近的Module
/// </summary>
/// <param name="sourcePos">源位置</param>
/// <param name="targetModule">最近的Module</param>
/// <returns>是否能找到目标范围内的Module</returns>
public bool TryFindClosestModule(Vector3 sourcePos, out BodyModule targetModule)
{
var closestDistance = Mathf.Infinity;
BodyModule closestModule = null;
if (BodyModules is not { Count: > 0 })
{
targetModule = null;
return false;
}
// 寻找最接近的BodyModule
foreach (var bodyModule in BodyModules)
{
if (!bodyModule.IsAvailable()) // 检查模块是否可用
{
continue; // 跳过不可用的模块
}
float tempDistance = Vector3.Distance(sourcePos, bodyModule.GetSocketPos().position);
if (tempDistance < closestDistance)
{
closestDistance = tempDistance;
closestModule = bodyModule;
}
}
bool res;
// 判断距离是否在目标范围内,在的话就返回,不在的话返回个空的
if (closestDistance < plugSnappingRange && closestModule)
{
targetModule = closestModule;
res = true;
}
else
{
targetModule = null;
res = false;
}
return res;
}
public void OpenModuleSlots()
{
AudioManager.Instance.PlaySfx("event:/ActionFB/mods_open");
foreach (var module in BodyModules)
{
module.OpenPlugSlot();
}
}
public void CloseModuleSlots([CanBeNull] BodyModule curModule)
{
if (BodyModules == null || BodyModules.Count == 0) return;
foreach (var module in BodyModules)
{
if (module != curModule)
{
module.ClosePlugSlot();
}
}
}
/// <summary>
/// 寻找指定名称的BodyModule
/// </summary>
/// <param name="modulename">模块名称</param>
/// <returns>对应名称的BodyModule</returns>
public BodyModule FindBodyModuleByName(string modulename)
{
return BodyModules?.FirstOrDefault(module => module.data.moduleName == modulename);
}
/// <summary>
/// 查找任意分组中的模块,包含 inactive 对象。用于可在模块显示前预设状态的 Yarn 命令。
/// </summary>
public BodyModule FindAnyBodyModuleByName(string moduleName)
{
return EnumerateAllBodyModules()
.FirstOrDefault(module => module.data.moduleName == moduleName);
}
/// <summary>
/// 寻找指定类型的BodyModule
/// </summary>
/// <typeparam name="T">模块类型</typeparam>
/// <returns>对应类型的BodyModule</returns>
public T FindBodyModuleByType<T>() where T : BodyModule
{
return BodyModules?.FirstOrDefault(module => module is T) as T;
}
/// <summary>
/// 设置发热。逻辑档位立即更新;视觉温度按热力图是否打开决定硬切/平滑,
/// 且降温动画已落到冷态附近时,Yarn 的 SetHeat(0) 不再把 0.5 硬切成 0。
/// </summary>
public void SetHeat(int targetHeatValue)
{
HeatValue = targetHeatValue;
if (heatMapController == null)
return;
// 降温协程已把视觉温度收到 ~0.5,随后对话里的 SetHeat(0) 只清逻辑,不冲掉视觉。
if (targetHeatValue <= 0 && heatMapController.Tempture <= 0.55f)
return;
bool mapVisible = heatMapController.IsHeatMapVisible;
if (mapVisible)
{
// 热力图已打开:升温/降温都走短 tween,避免档位硬切
float duration = targetHeatValue <= 0 ? 0.6f : 1.0f;
heatMapController.SetTemptureSmooth(targetHeatValue, duration);
}
else
{
// 面板未显示:直接落到目标,打开时不会再闪一下
heatMapController.SetTemptureDirect(targetHeatValue);
}
}
public void ShowHeatMap()
{
heatMapController.ShowHeatMap();
DisableSystem();
FixPanelSystem.GetRepairPanel<CablePanel>().ResetPlug();
}
public void HideHeatMap()
{
heatMapController.HideHeatMap();
EnableSystem();
}
/// <summary>
/// 冷却后处理
/// </summary>
public void OnCooling()
{
if (!inHotErr) return;
// 系统冷却
_heatValue = 0;
}
/// <summary>
/// 激活交互系统
/// </summary>
public void EnableSystem()
{
// isAvailable = true;
}
/// <summary>
/// 停止交互系统
/// </summary>
public void DisableSystem()
{
// isAvailable = false;
}
public void SwitchModuleGroup(ModuleState moduleState)
{
// 先切换Group
switch (moduleState)
{
case ModuleState.Body:
_bodyModuleGroup?.SetActive(true);
_headModuleGroup?.SetActive(false);
break;
case ModuleState.Head:
_bodyModuleGroup?.SetActive(false);
_headModuleGroup?.SetActive(true);
break;
default:
throw new ArgumentOutOfRangeException(nameof(moduleState), moduleState, null);
}
// 重新获取ModuleGroup
var root = moduleState == ModuleState.Body ? _bodyModuleGroup : _headModuleGroup;
BodyModules = root?.GetComponentsInChildren<BodyModule>().ToList();
_moduleState = moduleState;
}
/// <summary>
/// 从模块中清除
/// </summary>
/// <param name="module">将要被移除的模块</param>
public void RemoveModule(BodyModule module)
{
BodyModules.Remove(module);
}
private void Start()
{
// RepairSystemManager 的 Awake 已在所有场景对象的 Start 前完成。
RepairSystemManager.Register(this);
if (_headModuleGroup != null)
{
_headModuleOriginalPos = _headModuleGroup.transform.localPosition;
StartBreathing();
}
}
public void StartBreathing()
{
if (_breathingCoroutine != null)
{
StopCoroutine(_breathingCoroutine);
}
_breathingCoroutine = StartCoroutine(BreathingAnimation());
}
public void StopBreathing()
{
if (_breathingCoroutine != null)
{
StopCoroutine(_breathingCoroutine);
_breathingCoroutine = null;
}
if (_headModuleGroup != null)
{
_headModuleGroup.transform.localPosition = _headModuleOriginalPos;
}
}
private IEnumerator BreathingAnimation()
{
float time = 0;
while (true)
{
time += Time.deltaTime;
float yOffset = Mathf.Sin(time * 2f) * 0.02f; // 调整呼吸幅度和速度
_headModuleGroup.transform.localPosition = _headModuleOriginalPos + new Vector3(0, yOffset, 0);
yield return null;
}
// ReSharper disable once IteratorNeverReturns
}
public void TriggerPainShake()
{
if (_painShakeCoroutine != null)
{
StopCoroutine(_painShakeCoroutine);
}
_painShakeCoroutine = StartCoroutine(PainShakeAnimation());
}
private IEnumerator PainShakeAnimation()
{
StopBreathing();
float duration = 0.5f;
float elapsed = 0f;
Vector3 originalPos = _headModuleGroup.transform.localPosition;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float intensity = 1f - (elapsed / duration);
_headModuleGroup.transform.localPosition = originalPos + new Vector3(
UnityEngine.Random.Range(-0.1f, 0.1f) * intensity,
UnityEngine.Random.Range(-0.1f, 0.1f) * intensity,
UnityEngine.Random.Range(-0.1f, 0.1f) * intensity
);
yield return null;
}
_headModuleGroup.transform.localPosition = originalPos;
StartBreathing();
}
public void StartContinuousPain()
{
if (_continuousPainCoroutine != null)
{
StopCoroutine(_continuousPainCoroutine);
}
_continuousPainCoroutine = StartCoroutine(ContinuousPainAnimation());
}
public void StopContinuousPain()
{
if (_continuousPainCoroutine != null)
{
StopCoroutine(_continuousPainCoroutine);
_continuousPainCoroutine = null;
}
if (_headModuleGroup != null)
{
_headModuleGroup.transform.localPosition = _headModuleOriginalPos;
}
StartBreathing();
}
private IEnumerator ContinuousPainAnimation()
{
StopBreathing();
while (true)
{
// 抖动阶段
float shakeDuration = 1f;
float shakeElapsed = 0f;
Vector3 originalPos = _headModuleGroup.transform.localPosition;
while (shakeElapsed < shakeDuration)
{
shakeElapsed += Time.deltaTime;
_headModuleGroup.transform.localPosition = originalPos + new Vector3(
UnityEngine.Random.Range(-0.05f, 0.05f),
UnityEngine.Random.Range(-0.05f, 0.05f),
UnityEngine.Random.Range(-0.05f, 0.05f)
);
yield return null;
}
// 暂停阶段
_headModuleGroup.transform.localPosition = originalPos;
yield return new WaitForSeconds(0.5f);
}
// ReSharper disable once IteratorNeverReturns
}
}
public enum ModuleState
{
Body,
Head
}
}