using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections; public class lrPrefab : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler { [Header("uis")] public Text levelName; public Image levelid_btm; public Image statusImg; public Text thisSkillName; [Header("state visuals")] public Sprite lockedStatusSprite; public Color selectableSkillNameColor = Color.white; public Color unavailableSkillNameColor = Color.gray; [Header("desOBJ")] public GameObject descriptionPrefab; public float descriptionOffsetX = 80f; [Header("material")] public Material grayScale_mtr; [Header("preselect pulse")] public float preselectPulseDuration = 0.8f; private uLevel_skills owner; private userLevel_skills_SO ownerSo; private userLevel_skills_SO.UserLevelSkillEntry boundEntry; private int boundIndex = -1; private bool isUnlocked = false; private Coroutine hoverCoroutine; private Coroutine preselectPulseCoroutine; private GameObject spawnedDescription; private bool visualDefaultsCached = false; private Sprite defaultStatusSprite; private Material defaultStatusMaterial; private Material defaultLevelIdMaterial; public void Bind(uLevel_skills ownerController, userLevel_skills_SO sourceSo, int index, userLevel_skills_SO.UserLevelSkillEntry entry, bool unlocked) { CacheVisualDefaults(); owner = ownerController; ownerSo = sourceSo; boundIndex = index; boundEntry = entry; isUnlocked = unlocked; if (levelName != null) levelName.text = entry != null ? entry.userLevel.ToString() : string.Empty; RefreshVisualState(); } public void RefreshVisualState() { if (owner != null && boundEntry != null) isUnlocked = owner.IsSkillUnlocked(boundEntry); if (boundEntry == null) return; bool revealLockedSkills = PlayerSkillService.IsSkillEnabled(7); bool shouldHideLockedSkillInfo = !isUnlocked && !revealLockedSkills; if (thisSkillName != null) { thisSkillName.text = shouldHideLockedSkillInfo ? "?????" : (boundEntry.skillName ?? string.Empty); thisSkillName.color = isUnlocked ? selectableSkillNameColor : unavailableSkillNameColor; } if (statusImg == null) return; bool isPreselected = owner != null && owner.IsSkillPreselected(boundIndex); ApplyStatusState(isPreselected); } private void ApplyStatusState(bool isPreselected) { if (statusImg == null) return; StopPreselectPulse(); if (!isUnlocked) { SetStatusSprite(lockedStatusSprite != null ? lockedStatusSprite : defaultStatusSprite); SetStatusMaterial(defaultStatusMaterial); SetLevelIdMaterial(grayScale_mtr); SetStatusAlpha(1f); return; } SetStatusSprite(defaultStatusSprite); if (isPreselected) { SetStatusMaterial(defaultStatusMaterial); SetLevelIdMaterial(defaultLevelIdMaterial); StartPreselectPulse(); return; } if (boundEntry != null && boundEntry.isEnabled) { SetStatusMaterial(defaultStatusMaterial); SetLevelIdMaterial(defaultLevelIdMaterial); SetStatusAlpha(1f); return; } SetStatusMaterial(grayScale_mtr); SetLevelIdMaterial(defaultLevelIdMaterial); SetStatusAlpha(1f); } private void CacheVisualDefaults() { if (visualDefaultsCached) return; visualDefaultsCached = true; defaultStatusSprite = statusImg != null ? statusImg.sprite : null; defaultStatusMaterial = statusImg != null ? statusImg.material : null; defaultLevelIdMaterial = levelid_btm != null ? levelid_btm.material : null; } private void SetStatusSprite(Sprite spriteToApply) { if (statusImg == null) return; statusImg.sprite = spriteToApply; } private void SetStatusMaterial(Material materialToApply) { if (statusImg == null) return; statusImg.material = materialToApply; } private void SetLevelIdMaterial(Material materialToApply) { if (levelid_btm == null) return; levelid_btm.material = materialToApply; } private void SetStatusAlpha(float alpha) { if (statusImg == null) return; Color color = statusImg.color; color.a = Mathf.Clamp01(alpha); statusImg.color = color; } private void StartPreselectPulse() { if (!isActiveAndEnabled || statusImg == null) return; preselectPulseCoroutine = StartCoroutine(PreselectPulseRoutine()); } private void StopPreselectPulse() { if (preselectPulseCoroutine != null) { StopCoroutine(preselectPulseCoroutine); preselectPulseCoroutine = null; } } private IEnumerator PreselectPulseRoutine() { float duration = Mathf.Max(0.05f, preselectPulseDuration); float elapsed = 0f; while (true) { elapsed += Time.unscaledDeltaTime; float normalizedTime = Mathf.PingPong(elapsed / duration, 1f); SetStatusAlpha(Mathf.Lerp(0.5f, 1f, normalizedTime)); yield return null; } } public void OnPointerEnter(PointerEventData eventData) { if (boundEntry == null || descriptionPrefab == null) return; if (!isUnlocked && !PlayerSkillService.IsSkillEnabled(7)) return; if (hoverCoroutine != null) StopCoroutine(hoverCoroutine); hoverCoroutine = StartCoroutine(ShowDescriptionAfterDelay()); } public void OnPointerExit(PointerEventData eventData) { if (hoverCoroutine != null) { StopCoroutine(hoverCoroutine); hoverCoroutine = null; } HideDescription(); } public void OnPointerClick(PointerEventData eventData) { if (owner == null || boundIndex < 0) return; owner.ToggleSkill(boundIndex); } private IEnumerator ShowDescriptionAfterDelay() { yield return new WaitForSeconds(0.5f); hoverCoroutine = null; ShowDescription(); } private void ShowDescription() { if (spawnedDescription != null || descriptionPrefab == null || boundEntry == null) return; RectTransform root = ResolveDescriptionRoot(); if (root == null) return; spawnedDescription = Instantiate(descriptionPrefab, root); spawnedDescription.SetActive(true); spawnedDescription.transform.SetAsLastSibling(); esDesPrefab detail = spawnedDescription.GetComponent(); if (detail != null) detail.Bind(boundEntry.skillName, boundEntry.skillIcon, boundEntry.skillDescription, boundEntry.skillSummary); RectTransform spawnedRect = spawnedDescription.GetComponent(); if (spawnedRect == null) return; Canvas canvas = root.GetComponentInParent(); Camera eventCamera = null; if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay) eventCamera = canvas.worldCamera; Vector2 localPoint; RectTransformUtility.ScreenPointToLocalPointInRectangle(root, Input.mousePosition, eventCamera, out localPoint); localPoint.x += descriptionOffsetX; spawnedRect.anchoredPosition = localPoint; } private RectTransform ResolveDescriptionRoot() { Canvas canvas = GetComponentInParent(); if (canvas == null) return null; Canvas rootCanvas = canvas.rootCanvas != null ? canvas.rootCanvas : canvas; return rootCanvas.transform as RectTransform; } private void HideDescription() { if (spawnedDescription == null) return; if (Application.isPlaying) Destroy(spawnedDescription); else DestroyImmediate(spawnedDescription); spawnedDescription = null; } private void OnDisable() { StopPreselectPulse(); if (hoverCoroutine != null) { StopCoroutine(hoverCoroutine); hoverCoroutine = null; } HideDescription(); } }