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 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; /// 获取当前 BodyModule 分组(Body/Head)。 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(); } FixSystemCenter.SystemDic.Register(this); // 注册到维修系统管理器 RepairSystemManager.Register(this); } /// 捕获插线模块持久状态(分组 + UF + Chip + 高亮/报警)。 public BodyModuleSnapshotDto CaptureBodyModuleSnapshot() { var dto = new BodyModuleSnapshotDto { moduleState = _moduleState.ToString() }; var ufModule = FindBodyModuleByName("UF")?.GetComponent(); ufModule?.CaptureState(dto); var chipModule = FindBodyModuleByName("Color")?.GetComponent(); 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; } /// 写回插线模块持久状态;须在 fix cue 还原之后调用。 public void ApplyBodyModuleSnapshot(BodyModuleSnapshotDto dto) { if (dto == null) return; if (!string.IsNullOrEmpty(dto.moduleState) && Enum.TryParse(dto.moduleState, out var moduleState)) { SwitchModuleGroup(moduleState); } var ufModule = FindBodyModuleByName("UF")?.GetComponent(); ufModule?.ApplyState(dto); var chipModule = FindBodyModuleByName("Color")?.GetComponent(); chipModule?.ApplyState(dto); var highlighted = new HashSet(dto.highlightedModules ?? Enumerable.Empty()); var alarmed = new HashSet(dto.alarmedModules ?? Enumerable.Empty()); foreach (var module in EnumerateAllBodyModules()) { var name = module.data.moduleName; module.Highlight(highlighted.Contains(name)); module.Alarm(alarmed.Contains(name)); } } private IEnumerable EnumerateAllBodyModules() { if (_bodyModuleGroup != null) { foreach (var module in _bodyModuleGroup.GetComponentsInChildren(true)) yield return module; } if (_headModuleGroup != null) { foreach (var module in _headModuleGroup.GetComponentsInChildren(true)) yield return module; } } /// /// 寻找最接近的Module /// /// 源位置 /// 最近的Module /// 是否能找到目标范围内的Module 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(); } } } /// /// 寻找指定名称的BodyModule /// /// 模块名称 /// 对应名称的BodyModule public BodyModule FindBodyModuleByName(string modulename) { return BodyModules?.FirstOrDefault(module => module.data.moduleName == modulename); } /// /// 寻找指定类型的BodyModule /// /// 模块类型 /// 对应类型的BodyModule public T FindBodyModuleByType() where T : BodyModule { return BodyModules?.FirstOrDefault(module => module is T) as T; } /// /// 设置发热。逻辑档位立即更新;视觉温度按热力图是否打开决定硬切/平滑, /// 且降温动画已落到冷态附近时,Yarn 的 SetHeat(0) 不再把 0.5 硬切成 0。 /// 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().ResetPlug(); } public void HideHeatMap() { heatMapController.HideHeatMap(); EnableSystem(); } /// /// 冷却后处理 /// public void OnCooling() { if (!inHotErr) return; // 系统冷却 _heatValue = 0; } /// /// 激活交互系统 /// public void EnableSystem() { // isAvailable = true; } /// /// 停止交互系统 /// 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().ToList(); _moduleState = moduleState; } /// /// 从模块中清除 /// /// 将要被移除的模块 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 } }