using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour { [System.Serializable] public struct LevelConfig { public int pairCount; public int columnCount; public Vector2 cellSize; public float memorizeTime; public float limitTime; } [Header("Pengaturan Level")] public List levelList; [Header("UI Timer (Bar)")] public Image timerFill; public float penaltyTime = 3f; public float rewardTime = 2f; // FITUR BARU: Tambahan waktu 2 detik kalau tebakan benar private Color warnaAsliTimer; // Menyimpan warna asli UI hijau timer [Header("UI Win Popup")] public GameObject winPopup; public CanvasGroup dimmerCanvasGroup; public GameObject gameplayBoard; public Image[] starImages; public Sprite activeStar; public Sprite inactiveStar; public GameObject nextButton; [Header("Referensi UI & Objek")] public GameObject cardPrefab; public Transform gridParent; public List cardImages; public TextMeshProUGUI levelTitleText; [Header("Audio SFX")] public AudioSource sfxSource; public AudioClip klipBenar; public AudioClip klipSalah; [HideInInspector] public bool canClick = false; private List allCards = new List(); private List targetPositions = new List(); private Card firstSelected, secondSelected; private int pairsFound = 0; private float currentTime; private float maxTime; private bool isGameActive = false; void Start() { if (timerFill != null) warnaAsliTimer = timerFill.color; // Simpan warna timer di awal SetupCurrentLevel(); } void SetupCurrentLevel() { int idx = LevelData.SelectedLevel; if (levelList == null || idx >= levelList.Count) return; LevelConfig config = levelList[idx]; maxTime = (config.limitTime > 0) ? config.limitTime : 60f; currentTime = maxTime; pairsFound = 0; winPopup.SetActive(false); if (dimmerCanvasGroup != null) dimmerCanvasGroup.alpha = 0; if (gameplayBoard != null) gameplayBoard.SetActive(true); if (levelTitleText != null) levelTitleText.text = "Level " + (idx + 1); GridLayoutGroup gridLayout = gridParent.GetComponent(); gridLayout.constraintCount = config.columnCount; gridLayout.cellSize = config.cellSize; gridLayout.enabled = true; GenerateCards(config.pairCount); StartCoroutine(GameOpeningSequence(config.memorizeTime)); } void Update() { if (isGameActive) { currentTime -= Time.deltaTime; // Jaga biar waktu nggak minus if (currentTime < 0) currentTime = 0; if (timerFill != null) timerFill.fillAmount = currentTime / maxTime; if (currentTime <= 0) { currentTime = 0; isGameActive = false; // STOP SEGERA HandleGameOver(false); } } } public void CardSelected(Card selected) { if (!canClick) return; if (firstSelected == null) { firstSelected = selected; firstSelected.ShowCard(); } else if (secondSelected == null && selected != firstSelected) { secondSelected = selected; secondSelected.ShowCard(); StartCoroutine(CheckMatch()); } } IEnumerator CheckMatch() { canClick = false; yield return new WaitForSeconds(0.6f); if (firstSelected.GetSprite() == secondSelected.GetSprite()) { // --- JIKA TEBAKAN BENAR --- if (sfxSource != null && klipBenar != null) sfxSource.PlayOneShot(klipBenar); firstSelected.SetMatched(); secondSelected.SetMatched(); pairsFound++; // Cek apakah sudah menyelesaikan seluruh level if (pairsFound >= levelList[LevelData.SelectedLevel].pairCount) { HandleGameOver(true); } else { // --- INTEGRASI BARU: BERIKAN REWARD TAMBAHAN WAKTU --- currentTime += rewardTime; if (currentTime > maxTime) currentTime = maxTime; // Kunci agar tidak meluber dari batas maksimum bar StartCoroutine(AnimasiTimerHijau()); // Jalankan efek kedip hijau sukses } } else { // --- JIKA TEBAKAN SALAH --- if (sfxSource != null && klipSalah != null) sfxSource.PlayOneShot(klipSalah); currentTime -= penaltyTime; // Kurangi Waktu StartCoroutine(AnimasiTimerMerah()); // Panggil Efek Kedip Merah firstSelected.HideCard(); secondSelected.HideCard(); } firstSelected = null; secondSelected = null; canClick = true; } // --- FITUR ANIMASI KEDIP MERAH (KETIKA SALAH) --- IEnumerator AnimasiTimerMerah() { if (timerFill != null) { timerFill.color = Color.red; yield return new WaitForSeconds(0.2f); timerFill.color = warnaAsliTimer; } } // --- FITUR BARU: ANIMASI KEDIP HIJAU (REWARD KETIKA BENAR) --- IEnumerator AnimasiTimerHijau() { if (timerFill != null) { timerFill.color = Color.green; yield return new WaitForSeconds(0.2f); timerFill.color = warnaAsliTimer; } } void HandleGameOver(bool isWin) { if (winPopup.activeSelf) return; isGameActive = false; canClick = false; StopAllCoroutines(); StartCoroutine(GameOverSequence(isWin)); } IEnumerator GameOverSequence(bool isWin) { // Munculkan Popup dan Fade Blur winPopup.SetActive(true); winPopup.transform.localScale = Vector3.zero; float fadeTime = 0.3f; float elapsed = 0; while (elapsed < fadeTime) { elapsed += Time.deltaTime; float t = elapsed / fadeTime; if (dimmerCanvasGroup != null) dimmerCanvasGroup.alpha = t; winPopup.transform.localScale = Vector3.one * Mathf.SmoothStep(0, 1.1f, t); yield return null; } winPopup.transform.localScale = Vector3.one; if (gameplayBoard != null) gameplayBoard.SetActive(false); if (nextButton != null) nextButton.SetActive(isWin); int starCount = 0; int totalPairs = levelList[LevelData.SelectedLevel].pairCount; if (isWin) { starCount = 3; int nextIdx = LevelData.SelectedLevel + 2; if (nextIdx > PlayerPrefs.GetInt("ProgresMemory", 1)) { PlayerPrefs.SetInt("ProgresMemory", nextIdx); PlayerPrefs.Save(); } } else { float progress = (float)pairsFound / totalPairs; if (progress >= 0.8f) starCount = 2; else if (progress >= 0.4f) starCount = 1; else starCount = 0; } StartCoroutine(ShowStarSequence(starCount)); } IEnumerator ShowStarSequence(int count) { foreach (var s in starImages) { s.sprite = inactiveStar; s.transform.localScale = Vector3.one; } yield return new WaitForSeconds(0.4f); for (int i = 0; i < count; i++) { starImages[i].sprite = activeStar; starImages[i].transform.localScale = Vector3.zero; float st = 0; while (st < 1) { st += Time.deltaTime * 7f; starImages[i].transform.localScale = Vector3.one * Mathf.Lerp(0, 1.25f, st); yield return null; } starImages[i].transform.localScale = Vector3.one; yield return new WaitForSeconds(0.15f); } } public void ButtonRematch() => SceneManager.LoadScene(SceneManager.GetActiveScene().name); public void ButtonMenu() => SceneManager.LoadScene("MenuLevel"); public void ButtonNext() { if (LevelData.SelectedLevel < levelList.Count - 1) { LevelData.SelectedLevel++; SceneManager.LoadScene(SceneManager.GetActiveScene().name); } else ButtonMenu(); } void GenerateCards(int pairs) { foreach (Transform child in gridParent) Destroy(child.gameObject); allCards.Clear(); targetPositions.Clear(); List pool = new List(cardImages); List selectedIcons = new List(); int limit = Mathf.Min(pairs, pool.Count); for (int i = 0; i < limit; i++) { int r = Random.Range(0, pool.Count); selectedIcons.Add(pool[r]); pool.RemoveAt(r); } List pairList = new List(); foreach (Sprite s in selectedIcons) { pairList.Add(s); pairList.Add(s); } for (int i = 0; i < pairList.Count; i++) { Sprite temp = pairList[i]; int rand = Random.Range(i, pairList.Count); pairList[i] = pairList[rand]; pairList[rand] = temp; } foreach (Sprite img in pairList) { GameObject newCard = Instantiate(cardPrefab, gridParent); Card cardScript = newCard.GetComponent(); cardScript.SetCardData(img, this); allCards.Add(cardScript); newCard.transform.localScale = Vector3.zero; } } IEnumerator GameOpeningSequence(float memTime) { canClick = false; isGameActive = false; yield return new WaitForEndOfFrame(); GridLayoutGroup gridLayout = gridParent.GetComponent(); foreach (Card c in allCards) { targetPositions.Add(c.transform.localPosition); c.transform.localPosition = Vector3.zero; c.transform.localScale = Vector3.one; } gridLayout.enabled = false; float dealDuration = 0.4f; for (int i = 0; i < allCards.Count; i++) { StartCoroutine(MoveCardToPosition(allCards[i].transform, targetPositions[i], dealDuration)); yield return new WaitForSeconds(0.06f); } yield return new WaitForSeconds(dealDuration + 0.1f); foreach (Card c in allCards) c.ShowCard(); yield return new WaitForSeconds(memTime); foreach (Card c in allCards) c.HideCard(); yield return new WaitForSeconds(0.5f); isGameActive = true; canClick = true; } IEnumerator MoveCardToPosition(Transform cardTr, Vector3 target, float duration) { Vector3 startPos = cardTr.localPosition; float elapsed = 0; while (elapsed < duration) { elapsed += Time.deltaTime; float t = elapsed / duration; t = t * t * (3f - 2f * t); cardTr.localPosition = Vector3.Lerp(startPos, target, t); yield return null; } cardTr.localPosition = target; } }