447 lines
14 KiB
C#
447 lines
14 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);
|
|
|
|
// 注册到维修系统管理器
|
|
RepairSystemManager.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);
|
|
}
|
|
|
|
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>());
|
|
|
|
foreach (var module in EnumerateAllBodyModules())
|
|
{
|
|
var name = module.data.moduleName;
|
|
module.Highlight(highlighted.Contains(name));
|
|
module.Alarm(alarmed.Contains(name));
|
|
}
|
|
}
|
|
|
|
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>
|
|
/// 寻找指定类型的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>
|
|
/// 设置发热
|
|
/// </summary>
|
|
public void SetHeat(int targetHeatValue)
|
|
{
|
|
HeatValue = targetHeatValue;
|
|
|
|
heatMapController?.SetTemptureDirect(HeatValue);
|
|
}
|
|
|
|
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()
|
|
{
|
|
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
|
|
}
|
|
} |