using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class DragDropManager : MonoBehaviour { [System.Serializable] public struct LevelConfig { public string namaLevel; public int itemCount; public float maxTime; } [Header("Game Settings")] public List levelConfigs; public List spritePool; [Header("Prefabs")] public GameObject prefabSlotBayangan; public GameObject prefabSlotWarna; [Header("Containers")] public Transform shadowContainer; public Transform cardContainer; [Header("Timer UI")] public Image timerFill; public float penguranganWaktu = 3f; // Jumlah detik yang dikurangi kalau salah public float tambahanWaktu = 2f; // Jumlah detik yang ditambah kalau benar (BARU) private float currentTime, totalMaxTime; private Color warnaAsliTimer; // Untuk menyimpan warna hijau aslinya [Header("Win UI & Stars")] public GameObject winPopup; public GameObject[] activeStars; [Header("Audio SFX")] public AudioSource sfxSource; public AudioClip klipBenar; public AudioClip klipSalah; private int matchedCount = 0; private int goal; private bool isGameActive = false; void Start() { if (timerFill) warnaAsliTimer = timerFill.color; // Simpan warna aslinya SetupLevel(); } void Update() { if (isGameActive) { currentTime -= Time.deltaTime; // Jaga biar waktu nggak minus if (currentTime < 0) currentTime = 0; if (timerFill) timerFill.fillAmount = currentTime / totalMaxTime; if (currentTime <= 0) { isGameActive = false; StartCoroutine(ShowWinPopup(true)); } } } public void MainkanSuaraBenar() { if (sfxSource != null && klipBenar != null) { sfxSource.PlayOneShot(klipBenar); } } public void MainkanSuaraSalah() { if (sfxSource != null && klipSalah != null) { sfxSource.PlayOneShot(klipSalah); } } // --- FITUR BARU: REWARD FEEDBACK POSITIF --- public void BerikanBonusWaktu() { currentTime += tambahanWaktu; // Jaga agar penambahan waktu tidak meluber melebihi kapasitas maksimal Bar Timer if (currentTime > totalMaxTime) currentTime = totalMaxTime; StartCoroutine(TimerBerkedipHijau()); MainkanSuaraBenar(); // Otomatis bunyikan sfx benar } IEnumerator TimerBerkedipHijau() { if (timerFill != null) { timerFill.color = Color.green; // Flash warna hijau cerah sebagai reward yield return new WaitForSeconds(0.2f); timerFill.color = warnaAsliTimer; // Balik ke warna UI semula } } public void BerikanHukumanWaktu() { currentTime -= penguranganWaktu; StartCoroutine(TimerBerkedipMerah()); MainkanSuaraSalah(); // Otomatis bunyikan sfx salah } IEnumerator TimerBerkedipMerah() { if (timerFill != null) { timerFill.color = Color.red; // Berubah merah yield return new WaitForSeconds(0.2f); timerFill.color = warnaAsliTimer; // Balik ke warna semula } } void SetupLevel() { int lv = LevelData.SelectedLevel; if (lv >= levelConfigs.Count) lv = 0; LevelConfig config = levelConfigs[lv]; goal = config.itemCount; totalMaxTime = config.maxTime; currentTime = totalMaxTime; // ========================================================================= // FORMULA UTAMA: KALKULASI UKURAN KARTU DINAMIS (RESPONSIVE UI) // ========================================================================= GridLayoutGroup shadowGrid = shadowContainer.GetComponent(); RectTransform shadowRect = shadowContainer.GetComponent(); GridLayoutGroup cardGrid = cardContainer.GetComponent(); RectTransform cardRect = cardContainer.GetComponent(); float spacingX = 30f; Vector2 paddingDanSpacing = new Vector2(spacingX, 0); if (shadowGrid != null && shadowRect != null) { float lebarContainer = shadowRect.rect.width; float paddingKananKiri = 160f; float lebarTersedia = lebarContainer - paddingKananKiri - (spacingX * (goal - 1)); float dynamicWidth = lebarTersedia / goal; if (dynamicWidth > 240f) dynamicWidth = 240f; float dynamicHeight = dynamicWidth * 1.35f; Vector2 dynamicCellSize = new Vector2(dynamicWidth, dynamicHeight); shadowGrid.padding.left = 0; shadowGrid.padding.right = 0; shadowGrid.cellSize = dynamicCellSize; shadowGrid.spacing = paddingDanSpacing; shadowGrid.childAlignment = TextAnchor.MiddleCenter; shadowGrid.constraint = GridLayoutGroup.Constraint.FixedColumnCount; shadowGrid.constraintCount = goal; } if (cardGrid != null && cardRect != null) { cardGrid.padding.left = 0; cardGrid.padding.right = 0; cardGrid.cellSize = shadowGrid.cellSize; cardGrid.spacing = paddingDanSpacing; cardGrid.childAlignment = TextAnchor.MiddleCenter; cardGrid.constraint = GridLayoutGroup.Constraint.FixedColumnCount; cardGrid.constraintCount = goal; } // ========================================================================= List selected = new List(); List tempPool = new List(spritePool); for (int i = 0; i < goal; i++) { if (tempPool.Count == 0) break; int r = Random.Range(0, tempPool.Count); selected.Add(tempPool[r]); tempPool.RemoveAt(r); } StartCoroutine(SpawnSequence(selected)); } IEnumerator SpawnSequence(List selected) { for (int i = 0; i < selected.Count; i++) { GameObject s = Instantiate(prefabSlotBayangan, shadowContainer); ShadowSlot slot = s.GetComponentInChildren(); if (slot != null) slot.Setup(selected[i], i); s.transform.localScale = Vector3.zero; StartCoroutine(AnimasiSpawnKeren(s.transform)); yield return new WaitForSeconds(0.1f); } List shuffledIndices = new List(); for (int i = 0; i < selected.Count; i++) shuffledIndices.Add(i); for (int i = 0; i < shuffledIndices.Count; i++) { int temp = shuffledIndices[i]; int r = Random.Range(i, shuffledIndices.Count); shuffledIndices[i] = shuffledIndices[r]; shuffledIndices[r] = temp; } foreach (int idx in shuffledIndices) { GameObject c = Instantiate(prefabSlotWarna, cardContainer); DragDropItem ddi = c.GetComponentInChildren(); if (ddi != null) ddi.Setup(selected[idx], idx, this); c.transform.localScale = Vector3.zero; StartCoroutine(AnimasiSpawnKeren(c.transform)); yield return new WaitForSeconds(0.1f); } yield return new WaitForSeconds(0.2f); isGameActive = true; } IEnumerator AnimasiSpawnKeren(Transform t) { float time = 0; t.localRotation = Quaternion.Euler(0, 0, 180f); while (time < 1) { time += Time.deltaTime * 4; float scaleCrv = 1f + Mathf.Sin(time * Mathf.PI) * 0.2f; t.localScale = Vector3.one * (time < 1 ? time * scaleCrv : 1f); t.localRotation = Quaternion.Euler(0, 0, Mathf.Lerp(180f, 0f, time)); yield return null; } t.localScale = Vector3.one; t.localRotation = Quaternion.Euler(0, 0, 0); } public void CheckWin() { matchedCount++; if (matchedCount >= goal) { MainkanSuaraBenar(); // Putar sfx menang untuk kartu terakhir isGameActive = false; StartCoroutine(ShowWinPopup(false)); } else { BerikanBonusWaktu(); // Jika belum menang total, berikan bonus waktu + flash hijau + sfx benar } } IEnumerator ShowWinPopup(bool isTimeOut) { yield return new WaitForSeconds(0.5f); if (winPopup) { winPopup.SetActive(true); winPopup.transform.SetAsLastSibling(); } foreach (GameObject star in activeStars) { if (star != null) star.SetActive(false); } yield return new WaitForSeconds(0.3f); int starsEarned = 0; if (!isTimeOut) { float ratio = currentTime / totalMaxTime; if (ratio >= 0.6f) starsEarned = 3; else if (ratio >= 0.2f) starsEarned = 2; else starsEarned = 1; int levelTerbuka = PlayerPrefs.GetInt("ProgresDrag", 1); if (LevelData.SelectedLevel + 1 >= levelTerbuka) { PlayerPrefs.SetInt("ProgresDrag", LevelData.SelectedLevel + 2); } } else { float progress = (float)matchedCount / goal; if (progress >= 0.8f) starsEarned = 2; else if (progress >= 0.4f) starsEarned = 1; } for (int i = 0; i < starsEarned; i++) { if (i < activeStars.Length && activeStars[i] != null) { activeStars[i].transform.localScale = Vector3.zero; activeStars[i].SetActive(true); StartCoroutine(AnimasiSpawnKeren(activeStars[i].transform)); yield return new WaitForSeconds(0.3f); } } PlayerPrefs.SetInt("Stars_Drag_" + LevelData.SelectedLevel, starsEarned); } public void TombolRetry() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } public void TombolHome() { SceneManager.LoadScene("MenuLevel_DragDrop"); } public void TombolNext() { LevelData.SelectedLevel++; SceneManager.LoadScene(SceneManager.GetActiveScene().name); } }