using System; using AibisDream.Framework; using Newtonsoft.Json; using UnityEngine; using System.Collections; using DG.Tweening; using AibisDream.Kit; namespace AibisDream.FixSystem { public class BodyModule : MonoBehaviour, ISocket { #region 组件引用 private const string SocketObjName = "Socket"; private Transform _socketPos; private Transform _socketLeft; private Transform _socketRight; private Tween _socketTween; private BodyModuleSystem _bodyModuleSystem; private Material _highlightMaterial; private Material _alarmMaterial; private Material _originalMaterial; // 原材质 /// /// Yarn 可能在 Start 里异步加载材质完成前就调用 ModuleHightLight,需记下来待加载后再套材质。 /// private bool _wantsHighlight; private bool _wantsAlarm; public bool IsHighlighted => _wantsHighlight; public bool IsAlarmed => _wantsAlarm; [SerializeField] private SpriteRenderer targetSpriteRenderer; // 目标的 SpriteRenderer [Header("模块运行状态")] [SerializeField] private ModuleConditionVisual conditionVisual; [SerializeField] private ModuleCondition condition = ModuleCondition.Normal; public ModuleCondition Condition => condition; public bool SupportsConditionVisual => conditionVisual != null && conditionVisual.IsConfigured; private SpriteRenderer _cutSpriteRenderer; private SpriteRenderer _cutTextRenderer; // 添加cutText的SpriteRenderer引用 [Header("CutText闪烁效果")] private float cutTextFlickerThreshold = 0.4f; // 开始闪烁的阈值 private float cutTextFlickerInterval = 0.1f; // 闪烁间隔 private bool isCutTextFlickering = false; // 是否正在闪烁 private Coroutine flickerCoroutine; // 闪烁协程引用 #endregion // 模块基本数据 [SerializeField] public BodyModuleData data; public bool available = true; private bool _isOpen; private void Awake() { // 获取组件索引 InitReference(); } private void Start() { StartCoroutine(LoadMaterial()); conditionVisual?.ApplyCondition(condition); } protected virtual void InitReference() { _socketPos = transform.Find(SocketObjName); _socketLeft = _socketPos.Find("Left"); _socketRight = _socketPos.Find("Right"); _bodyModuleSystem = transform.parent.parent.parent.GetComponent(); if (targetSpriteRenderer == null) { targetSpriteRenderer = transform.Find("Module Pic").GetComponent(); } _originalMaterial = targetSpriteRenderer.GetComponent().material; _cutSpriteRenderer = transform.Find("Cut Pic")?.GetComponent(); if (_cutSpriteRenderer != null) { // 初始化cutText的SpriteRenderer _cutTextRenderer = _cutSpriteRenderer.transform.Find("Cut Text")?.GetComponent(); } } private IEnumerator LoadMaterial() { // 异步加载材质 var highlightHandle = ResourceSystem.LoadAsync(FixAssetRef.HighlightMaterial); var alarmHandle = ResourceSystem.LoadAsync(FixAssetRef.AlarmMaterial); yield return highlightHandle; yield return alarmHandle; if (highlightHandle.Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded) _highlightMaterial = highlightHandle.Result; else Debug.LogError("[BodyModule] Failed to load HighlightMaterial"); if (alarmHandle.Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded) _alarmMaterial = alarmHandle.Result; else Debug.LogError("[BodyModule] Failed to load AlarmMaterial"); ApplyModuleVisual(); } #region 插槽功能 public bool IsAvailable() { return available; } public void SetSocketAvailable(bool isAvailable) { available = isAvailable; } public virtual void PlugIn() { if (DialogController.Instance) // 然后触发Yarn节点 DialogController.Instance.StartDialogNode(_bodyModuleSystem.inHotErr ? "系统发热" : data.PlugInNodeName); } public virtual void PlugOut() { } public Transform GetSocketPos() { return _socketPos; } public void OpenPlugSlot() { if (!_socketLeft) return; if (_isOpen) return; _isOpen = true; // 打开插槽 if (_socketTween.IsActive() && _socketTween.IsPlaying()) _socketTween.Kill(true); var sequence = DOTween.Sequence(); sequence.Join(_socketLeft.DOScale(new Vector3(0, 1, 1), 0.2f).SetEase(Ease.InSine)); sequence.Join(_socketRight.DOScale(new Vector3(0, 1, 1), 0.2f).SetEase(Ease.InSine)); _socketTween = sequence; } public void ClosePlugSlot() { if (!_socketLeft) return; if (!_isOpen) return; _isOpen = false; // 关闭插槽 if (_socketTween.IsActive() && _socketTween.IsPlaying()) _socketTween.Kill(true); var sequence = DOTween.Sequence(); sequence.Join(_socketLeft.DOScale(new Vector3(1, 1, 1), 0.2f).SetEase(Ease.OutSine)); sequence.Join(_socketRight.DOScale(new Vector3(1, 1, 1), 0.2f).SetEase(Ease.OutSine)); _socketTween = sequence; } #endregion #region 高亮提示和隐藏 public void Highlight(bool enable) { _wantsHighlight = enable; ApplyModuleVisual(); } public void Alarm(bool enable) { _wantsAlarm = enable; ApplyModuleVisual(); } private void ApplyModuleVisual() { if (targetSpriteRenderer == null) return; if (_wantsAlarm && _alarmMaterial != null) targetSpriteRenderer.material = _alarmMaterial; else if (_wantsHighlight && _highlightMaterial != null) targetSpriteRenderer.material = _highlightMaterial; else targetSpriteRenderer.material = _originalMaterial; } #endregion #region 模块运行状态 /// /// 设置模块表现状态。状态只影响独立文字与火花,不影响 Module Pic、插线或切割逻辑。 /// public bool SetCondition(ModuleCondition newCondition) { if (conditionVisual == null) { Debug.LogWarning($"[BodyModule] Module {data.moduleName} has no ModuleConditionVisual configured.", this); return false; } condition = newCondition; conditionVisual.ApplyCondition(newCondition); return true; } #endregion #region 切割相关 public void OnCuttingDown() { _bodyModuleSystem.RemoveModule(this); DialogController.Instance?.StartDialogNode($"{data.moduleName}CutDown"); EnumEventSystem.Global.Send(EventEnum.CutEnd); Destroy(gameObject); } public void SetCutTextVisible(bool visible) { if (_cutTextRenderer != null) { _cutTextRenderer.enabled = visible; } } /// /// 按当前 ModuleCondition 同步 CutText 亮灭,与舱外 ModuleText 表现一致。 /// Normal 常亮 / Damaged 闪烁 / PoweredOff 熄灭。 /// public void ApplyCutTextFromCondition() { // 没有独立 CutText 的模块(如无特写文字的模块)直接跳过,避免起无效闪烁协程。 if (_cutTextRenderer == null) return; StopCutTextFlicker(restoreVisible: false); switch (condition) { case ModuleCondition.Normal: SetCutTextVisible(true); break; case ModuleCondition.Damaged: // 与舱外文字当前的瞬时亮灭相位对齐,而非固定为常亮,避免入场瞬间闪烁不同步 bool currentlyVisible = conditionVisual == null || conditionVisual.IsTextCurrentlyVisible; SetCutTextVisible(currentlyVisible); StartCutTextFlicker(); break; case ModuleCondition.PoweredOff: SetCutTextVisible(false); break; } } public SpriteRenderer GetCutSpriteRenderer() { return _cutSpriteRenderer; } public SpriteRenderer GetCutTextSpriteRenderer() { return _cutTextRenderer; } public void StartCutTextFlicker() { if (!isCutTextFlickering) { isCutTextFlickering = true; if (flickerCoroutine != null) { StopCoroutine(flickerCoroutine); } flickerCoroutine = StartCoroutine(CutTextFlickerCoroutine()); } } public void StopCutTextFlicker(bool restoreVisible = true) { isCutTextFlickering = false; if (flickerCoroutine != null) { StopCoroutine(flickerCoroutine); flickerCoroutine = null; } if (restoreVisible) SetCutTextVisible(true); } private IEnumerator CutTextFlickerCoroutine() { while (isCutTextFlickering) { // 随机决定是否显示 bool shouldShow = UnityEngine.Random.value > 0.5f; SetCutTextVisible(shouldShow); // 播放相应的音效 if (shouldShow) { AudioManager.Instance.PlaySfx("event:/ActionFB/light_flicker_on"); } else { AudioManager.Instance.PlaySfx("event:/ActionFB/light_flicker_off"); } // 随机等待时间,模拟接触不良效果 float waitTime = UnityEngine.Random.Range(cutTextFlickerInterval * 0.5f, cutTextFlickerInterval * 1.5f); yield return new WaitForSeconds(waitTime); } } public void UpdateCutProgress(float progress) { // 检查是否达到闪烁阈值 if (!isCutTextFlickering && progress >= cutTextFlickerThreshold) { StartCutTextFlicker(); } } #endregion } [Serializable] public struct BodyModuleData { public const string PlugInNodeTemplate = "{0}PlugIn"; // ID public string moduleName; // 只读 [JsonIgnore] public string PlugInNodeName => string.Format(PlugInNodeTemplate, moduleName); } public enum ModuleCondition { Normal, Damaged, PoweredOff } }