using UnityEngine; using UnityEngine.UI; using TMPro; using UnityEngine.SceneManagement; using System.Collections; using System.Collections.Generic; public class ParagraphReading24Mod : MonoBehaviour { [Header("DATA")] public TextAsset wordFile; public string wordsResourcePath = "Mod24Words"; [Header("MAIN TEXTS")] [Tooltip("Paragraf burada gösterilecek.")] public TMP_Text paragraphText; [Tooltip("Paragrafa bağlı soru burada gösterilecek.")] public TMP_Text questionText; [Header("TEXTS")] public TMP_Text titleText; public TMP_Text languageText; public TMP_Text feedbackText; public TMP_Text scoreText; public TMP_Text comboText; public TMP_Text timerText; [Header("OPTIONAL STATS")] public TMP_Text questionCounterText; public TMP_Text accuracyText; public TMP_Text bestComboText; [Header("ANSWER BUTTONS")] public Button[] answerButtons; public TMP_Text[] answerTexts; [Header("MAIN BUTTONS")] public Button submitButton; public Button backButton; [Header("EXTRA BUTTONS")] public Button extraTimeButton; public Button extraHintButton; public Button extraSkipButton; public TMP_Text extraTimeCooldownText; public TMP_Text extraHintCooldownText; public TMP_Text extraSkipCooldownText; [Header("SCENES")] public string backSceneName = "modsec 24"; public string resultSceneName = "modres 24"; [Header("LANGUAGE TEST")] [Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | QuestionLangID = paragraf ve soru dili")] public int forcedQuestionLanguageIndex = -1; [Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | AnswerLangID = şıkların dili")] public int forcedAnswerLanguageIndex = -1; [Header("GAME SETTINGS")] public float startTime = 60f; public bool useSelectedGameTime = true; public int basePoints = 50; public int wrongPenalty = 50; public float wrongTimePenalty = 0f; public float correctTimeBonus = 2f; public float nextQuestionDelay = 1.25f; [Header("EXTRA SETTINGS")] public float extraTimeAmount = 5f; public float cooldownDuration = 5f; [Header("COMBO MULTIPLIERS")] public float mult_1to2 = 1f; public float mult_3to4 = 1.15f; public float mult_5to7 = 1.3f; public float mult_8plus = 1.5f; [Header("COLORS")] public Color normalButtonColor = new Color32(120, 70, 220, 255); public Color selectedButtonColor = new Color32(155, 105, 255, 255); public Color correctButtonColor = new Color32(70, 240, 130, 255); public Color wrongButtonColor = new Color32(255, 80, 95, 255); public Color disabledButtonColor = new Color32(90, 75, 120, 255); readonly string[] langNames = { "Türkçe", "English", "Español", "Deutsch", "Français", "Português" }; const string modKey = "Mod24"; class ParagraphEntry { public string[] paragraphs = new string[6]; public string[] questions = new string[6]; // Her dil için 4 şık: // 0 doğru // 1 yanlış // 2 yanlış // 3 yanlış public string[] options = new string[24]; } readonly List allQuestions = new List(); readonly List questionPool = new List(); ParagraphEntry currentQuestion; int paragraphLangIndex = 1; int optionLangIndex = 0; int selectedAnswer = -1; int correctAnswerIndex = -1; int questionNumber = 0; int score = 0; int combo = 0; int correctCount = 0; int wrongCount = 0; int bestComboThisRun = 0; float timeLeft; bool gameOver = false; bool waitingNextQuestion = false; bool extraTimeReady = true; bool extraHintReady = true; bool extraSkipReady = true; Vector3[] originalButtonScales; Vector3[] originalButtonPositions; void Awake() { LoadQuestions(); } void Start() { ResolveLanguages(); timeLeft = startTime; if (useSelectedGameTime) { float selectedTime = PlayerPrefs.GetFloat("SelectedGameTime", startTime); if (selectedTime > 0f) timeLeft = selectedTime; } if (timeLeft <= 0f) timeLeft = 60f; if (allQuestions.Count < 1) { Debug.LogError("Mod24: En az 1 geçerli TXT satırı gerekli. Yüklenen soru sayısı: " + allQuestions.Count); enabled = false; return; } if (paragraphText == null) { Debug.LogError("Mod24: paragraphText atanmadı."); enabled = false; return; } if (questionText == null) { Debug.LogError("Mod24: questionText atanmadı."); enabled = false; return; } if (answerButtons == null || answerButtons.Length < 4 || answerTexts == null || answerTexts.Length < 4) { Debug.LogError("Mod24: 4 answerButtons ve 4 answerTexts atanmalı."); enabled = false; return; } PrepareUI(); BindButtons(); ResetQuestionPool(); UpdateLanguageUI(); UpdateScoreUI(); UpdateComboUI(); UpdateTimerUI(); UpdateStatsUI(); NextQuestion(); Debug.Log("Mod24 Paragraf Dili: " + paragraphLangIndex + " / " + langNames[paragraphLangIndex]); Debug.Log("Mod24 Şık Dili: " + optionLangIndex + " / " + langNames[optionLangIndex]); } void Update() { if (gameOver) return; timeLeft -= Time.deltaTime; if (timeLeft <= 0f) { timeLeft = 0f; UpdateTimerUI(); GameOver(); return; } UpdateTimerUI(); } // TXT FORMAT: // 0-5 paragraf: // trParagraph|enParagraph|esParagraph|deParagraph|frParagraph|ptParagraph // // 6-11 soru: // trQuestion|enQuestion|esQuestion|deQuestion|frQuestion|ptQuestion // // 12-35 cevap şıkları: // trCorrect|trWrong1|trWrong2|trWrong3 // enCorrect|enWrong1|enWrong2|enWrong3 // esCorrect|esWrong1|esWrong2|esWrong3 // deCorrect|deWrong1|deWrong2|deWrong3 // frCorrect|frWrong1|frWrong2|frWrong3 // ptCorrect|ptWrong1|ptWrong2|ptWrong3 void LoadQuestions() { allQuestions.Clear(); TextAsset txt = wordFile; if (txt == null) txt = Resources.Load(wordsResourcePath); if (txt == null) { Debug.LogError("Mod24: TXT bulunamadı. Şuraya koy: Assets/Resources/" + wordsResourcePath + ".txt"); return; } string[] lines = txt.text.Split( new[] { '\n', '\r' }, System.StringSplitOptions.RemoveEmptyEntries ); for (int i = 0; i < lines.Length; i++) { string line = lines[i].Trim(); if (string.IsNullOrWhiteSpace(line)) continue; if (line.StartsWith("#")) continue; string[] p = SplitLineSmart(line); if (p.Length < 36) { Debug.LogWarning("Mod24 TXT eksik satır atlandı. Satır: " + (i + 1) + " Alan sayısı: " + p.Length); continue; } ParagraphEntry entry = new ParagraphEntry(); bool valid = true; for (int l = 0; l < 6; l++) { entry.paragraphs[l] = CleanField(p[l]); if (string.IsNullOrWhiteSpace(entry.paragraphs[l])) valid = false; } for (int l = 0; l < 6; l++) { entry.questions[l] = CleanField(p[6 + l]); if (string.IsNullOrWhiteSpace(entry.questions[l])) valid = false; } for (int q = 0; q < 24; q++) { entry.options[q] = CleanField(p[12 + q]); if (string.IsNullOrWhiteSpace(entry.options[q])) valid = false; } if (valid) allQuestions.Add(entry); } Debug.Log("Mod24 yüklenen soru sayısı: " + allQuestions.Count); } string[] SplitLineSmart(string line) { if (line.Contains("|")) return line.Split('|'); if (line.Contains("\t")) return line.Split('\t'); return line.Split(','); } string CleanField(string s) { if (string.IsNullOrEmpty(s)) return ""; return s.Trim().Trim('"').Trim().Replace("\\n", "\n").Replace("[blank]", "____"); } void ResolveLanguages() { // QuestionLangID = ekrandaki paragraf ve soru dili // AnswerLangID = şıklardaki cevap dili if (forcedQuestionLanguageIndex >= 0 && forcedQuestionLanguageIndex <= 5) paragraphLangIndex = forcedQuestionLanguageIndex; else paragraphLangIndex = PlayerPrefs.GetInt("QuestionLangID", 1); if (forcedAnswerLanguageIndex >= 0 && forcedAnswerLanguageIndex <= 5) optionLangIndex = forcedAnswerLanguageIndex; else optionLangIndex = PlayerPrefs.GetInt("AnswerLangID", 0); paragraphLangIndex = ClampLanguage(paragraphLangIndex, 1); optionLangIndex = ClampLanguage(optionLangIndex, 0); } int ClampLanguage(int value, int fallback) { if (value < 0 || value > 5) return fallback; return value; } public void RefreshLanguagesFromPrefs() { ResolveLanguages(); UpdateLanguageUI(); if (currentQuestion != null && !gameOver) RefreshCurrentQuestion(); } public void SetQuestionLanguage(int id) { if (id < 0 || id > 5) return; paragraphLangIndex = id; PlayerPrefs.SetInt("QuestionLangID", id); PlayerPrefs.SetString("QuestionLangCode", ToCode(id)); PlayerPrefs.Save(); UpdateLanguageUI(); RefreshCurrentQuestion(); } public void SetAnswerLanguage(int id) { if (id < 0 || id > 5) return; optionLangIndex = id; PlayerPrefs.SetInt("AnswerLangID", id); PlayerPrefs.SetString("AnswerLangCode", ToCode(id)); PlayerPrefs.Save(); UpdateLanguageUI(); RefreshCurrentQuestion(); } string ToCode(int id) { switch (id) { case 0: return "tr"; case 1: return "en"; case 2: return "es"; case 3: return "de"; case 4: return "fr"; case 5: return "pt"; default: return "en"; } } void UpdateLanguageUI() { if (languageText != null) { languageText.text = "PARAGRAF: " + langNames[paragraphLangIndex] + " | CEVAP: " + langNames[optionLangIndex]; } if (titleText != null) titleText.text = GetTitleText(); } string GetTitleText() { switch (optionLangIndex) { case 0: return "Paragrafı Oku ve Cevapla"; case 1: return "Read and Answer"; case 2: return "Lee y responde"; case 3: return "Lies und antworte"; case 4: return "Lis et réponds"; case 5: return "Leia e responda"; default: return "Read and Answer"; } } string GetText(string key) { int lang = optionLangIndex; if (key == "choose") { if (lang == 0) return "BİR CEVAP SEÇ"; if (lang == 1) return "CHOOSE AN ANSWER"; if (lang == 2) return "ELIGE UNA RESPUESTA"; if (lang == 3) return "WÄHLE EINE ANTWORT"; if (lang == 4) return "CHOISIS UNE RÉPONSE"; if (lang == 5) return "ESCOLHA UMA RESPOSTA"; } if (key == "correct") { if (lang == 0) return "DOĞRU"; if (lang == 1) return "CORRECT"; if (lang == 2) return "CORRECTO"; if (lang == 3) return "RICHTIG"; if (lang == 4) return "CORRECT"; if (lang == 5) return "CORRETO"; } if (key == "wrong") { if (lang == 0) return "YANLIŞ"; if (lang == 1) return "WRONG"; if (lang == 2) return "INCORRECTO"; if (lang == 3) return "FALSCH"; if (lang == 4) return "FAUX"; if (lang == 5) return "ERRADO"; } if (key == "correctAnswer") { if (lang == 0) return "Doğru cevap"; if (lang == 1) return "Correct answer"; if (lang == 2) return "Respuesta correcta"; if (lang == 3) return "Richtige Antwort"; if (lang == 4) return "Bonne réponse"; if (lang == 5) return "Resposta correta"; } if (key == "paragraphLabel") { if (paragraphLangIndex == 0) return "Paragraf"; if (paragraphLangIndex == 1) return "Paragraph"; if (paragraphLangIndex == 2) return "Párrafo"; if (paragraphLangIndex == 3) return "Absatz"; if (paragraphLangIndex == 4) return "Paragraphe"; if (paragraphLangIndex == 5) return "Parágrafo"; } if (key == "questionLabel") { if (paragraphLangIndex == 0) return "Soru"; if (paragraphLangIndex == 1) return "Question"; if (paragraphLangIndex == 2) return "Pregunta"; if (paragraphLangIndex == 3) return "Frage"; if (paragraphLangIndex == 4) return "Question"; if (paragraphLangIndex == 5) return "Pergunta"; } if (key == "skipped") { if (lang == 0) return "GEÇİLDİ"; if (lang == 1) return "SKIPPED"; if (lang == 2) return "SALTADO"; if (lang == 3) return "ÜBERSPRUNGEN"; if (lang == 4) return "PASSÉ"; if (lang == 5) return "PULADO"; } return key; } void PrepareUI() { if (paragraphText != null) { paragraphText.alignment = TextAlignmentOptions.Center; paragraphText.enableWordWrapping = true; } if (questionText != null) { questionText.alignment = TextAlignmentOptions.Center; questionText.enableWordWrapping = true; } if (feedbackText != null) { feedbackText.text = ""; feedbackText.alignment = TextAlignmentOptions.Center; feedbackText.enableWordWrapping = true; } originalButtonScales = new Vector3[answerButtons.Length]; originalButtonPositions = new Vector3[answerButtons.Length]; for (int i = 0; i < answerButtons.Length; i++) { if (answerButtons[i] == null) continue; originalButtonScales[i] = answerButtons[i].transform.localScale; originalButtonPositions[i] = answerButtons[i].transform.localPosition; answerButtons[i].transition = Selectable.Transition.None; if (answerButtons[i].image != null) answerButtons[i].image.color = FullAlpha(normalButtonColor); } HideCooldownTexts(); } void HideCooldownTexts() { if (extraTimeCooldownText != null) extraTimeCooldownText.gameObject.SetActive(false); if (extraHintCooldownText != null) extraHintCooldownText.gameObject.SetActive(false); if (extraSkipCooldownText != null) extraSkipCooldownText.gameObject.SetActive(false); } void BindButtons() { if (submitButton != null) { submitButton.onClick.RemoveAllListeners(); submitButton.onClick.AddListener(OnSubmit); } if (backButton != null) { backButton.onClick.RemoveAllListeners(); backButton.onClick.AddListener(() => { SaveProgressOnly(); SceneManager.LoadScene(backSceneName); }); } if (extraTimeButton != null) { extraTimeButton.onClick.RemoveAllListeners(); extraTimeButton.onClick.AddListener(OnExtraTime); } if (extraHintButton != null) { extraHintButton.onClick.RemoveAllListeners(); extraHintButton.onClick.AddListener(OnExtraHint); } if (extraSkipButton != null) { extraSkipButton.onClick.RemoveAllListeners(); extraSkipButton.onClick.AddListener(OnExtraSkip); } for (int i = 0; i < answerButtons.Length; i++) { int index = i; if (answerButtons[i] == null) continue; answerButtons[i].onClick.RemoveAllListeners(); answerButtons[i].onClick.AddListener(() => SelectAnswer(index)); } } void ResetQuestionPool() { questionPool.Clear(); questionPool.AddRange(allQuestions); Shuffle(questionPool); } void NextQuestion() { if (gameOver) return; if (questionPool.Count <= 0) ResetQuestionPool(); currentQuestion = questionPool[0]; questionPool.RemoveAt(0); questionNumber++; RefreshCurrentQuestion(); } void RefreshCurrentQuestion() { if (currentQuestion == null) return; waitingNextQuestion = false; selectedAnswer = -1; correctAnswerIndex = -1; ResetButtons(); string paragraphValue = currentQuestion.paragraphs[paragraphLangIndex]; string questionValue = currentQuestion.questions[paragraphLangIndex]; string correctAnswer = GetOption(currentQuestion, optionLangIndex, 0); if (paragraphText != null) paragraphText.text = GetText("paragraphLabel") + ":\n" + paragraphValue; if (questionText != null) questionText.text = GetText("questionLabel") + ":\n" + questionValue; List options = new List(); for (int i = 0; i < 4; i++) { string option = GetOption(currentQuestion, optionLangIndex, i); if (!string.IsNullOrWhiteSpace(option)) options.Add(option); } while (options.Count < 4) options.Add("?"); Shuffle(options); correctAnswerIndex = options.IndexOf(correctAnswer); if (correctAnswerIndex < 0) { correctAnswerIndex = 0; options[0] = correctAnswer; } for (int i = 0; i < 4; i++) { if (answerTexts != null && i < answerTexts.Length && answerTexts[i] != null) answerTexts[i].text = options[i]; } if (feedbackText != null) feedbackText.text = ""; if (submitButton != null) submitButton.interactable = true; UpdateStatsUI(); StartCoroutine(MainTextPopAnim()); } string GetOption(ParagraphEntry entry, int langIndex, int optionIndex) { int index = langIndex * 4 + optionIndex; if (index < 0 || index >= entry.options.Length) return ""; return entry.options[index]; } void ResetButtons() { for (int i = 0; i < answerButtons.Length; i++) { if (answerButtons[i] == null) continue; answerButtons[i].interactable = true; answerButtons[i].transform.localScale = originalButtonScales[i]; answerButtons[i].transform.localPosition = originalButtonPositions[i]; if (answerButtons[i].image != null) answerButtons[i].image.color = FullAlpha(normalButtonColor); if (answerTexts != null && i < answerTexts.Length && answerTexts[i] != null) answerTexts[i].text = ""; } } void SelectAnswer(int index) { if (gameOver || waitingNextQuestion) return; if (index < 0 || index >= answerButtons.Length) return; if (answerButtons[index] == null || !answerButtons[index].interactable) return; selectedAnswer = index; for (int i = 0; i < answerButtons.Length; i++) { if (answerButtons[i] == null) continue; if (answerButtons[i].image != null) answerButtons[i].image.color = FullAlpha(normalButtonColor); answerButtons[i].transform.localScale = originalButtonScales[i]; } if (answerButtons[index].image != null) answerButtons[index].image.color = FullAlpha(selectedButtonColor); answerButtons[index].transform.localScale = originalButtonScales[index] * 0.94f; } void OnSubmit() { if (gameOver || waitingNextQuestion) return; if (selectedAnswer < 0) { ShowFeedback(GetText("choose"), wrongButtonColor); return; } waitingNextQuestion = true; if (submitButton != null) submitButton.interactable = false; for (int i = 0; i < answerButtons.Length; i++) { if (answerButtons[i] != null) answerButtons[i].interactable = false; } if (selectedAnswer == correctAnswerIndex) CorrectAnswer(); else WrongAnswer(); UpdateScoreUI(); UpdateComboUI(); UpdateStatsUI(); SaveProgressOnly(); StartCoroutine(NextQuestionDelay()); } void CorrectAnswer() { if (answerButtons[selectedAnswer].image != null) answerButtons[selectedAnswer].image.color = FullAlpha(correctButtonColor); combo++; correctCount++; if (combo > bestComboThisRun) bestComboThisRun = combo; int earned = Mathf.RoundToInt(basePoints * GetComboMultiplier(combo)); score += earned; timeLeft += correctTimeBonus; ShowFeedback(GetText("correct") + " +" + earned, correctButtonColor); StartCoroutine(ButtonPop(answerButtons[selectedAnswer])); } void WrongAnswer() { if (answerButtons[selectedAnswer].image != null) answerButtons[selectedAnswer].image.color = FullAlpha(wrongButtonColor); if (correctAnswerIndex >= 0 && correctAnswerIndex < answerButtons.Length) { if (answerButtons[correctAnswerIndex].image != null) answerButtons[correctAnswerIndex].image.color = FullAlpha(correctButtonColor); } wrongCount++; combo = 0; score -= wrongPenalty; if (score < 0) score = 0; if (wrongTimePenalty > 0f) { timeLeft -= wrongTimePenalty; if (timeLeft < 0f) timeLeft = 0f; } string correctText = ""; if (correctAnswerIndex >= 0 && correctAnswerIndex < answerTexts.Length && answerTexts[correctAnswerIndex] != null) correctText = answerTexts[correctAnswerIndex].text; ShowFeedback(GetText("wrong") + "\n" + GetText("correctAnswer") + ": " + correctText, wrongButtonColor); if (selectedAnswer >= 0 && selectedAnswer < answerButtons.Length) StartCoroutine(ButtonShake(answerButtons[selectedAnswer])); } IEnumerator NextQuestionDelay() { yield return new WaitForSeconds(nextQuestionDelay); if (!gameOver) NextQuestion(); } void OnExtraTime() { if (!extraTimeReady || gameOver) return; timeLeft += extraTimeAmount; extraTimeReady = false; if (extraTimeButton != null) extraTimeButton.interactable = false; StartCoroutine(Cooldown(extraTimeCooldownText, extraTimeButton, () => extraTimeReady = true)); } void OnExtraHint() { if (!extraHintReady || gameOver || waitingNextQuestion) return; int removed = 0; for (int i = 0; i < answerButtons.Length && removed < 2; i++) { if (i == correctAnswerIndex) continue; if (i == selectedAnswer) continue; if (answerButtons[i] == null) continue; if (!answerButtons[i].interactable) continue; answerButtons[i].interactable = false; if (answerButtons[i].image != null) answerButtons[i].image.color = FullAlpha(disabledButtonColor); if (answerTexts != null && i < answerTexts.Length && answerTexts[i] != null) answerTexts[i].text = "—"; removed++; } extraHintReady = false; if (extraHintButton != null) extraHintButton.interactable = false; StartCoroutine(Cooldown(extraHintCooldownText, extraHintButton, () => extraHintReady = true)); } void OnExtraSkip() { if (!extraSkipReady || gameOver || waitingNextQuestion) return; combo = 0; UpdateComboUI(); ShowFeedback(GetText("skipped"), Color.white); NextQuestion(); extraSkipReady = false; if (extraSkipButton != null) extraSkipButton.interactable = false; StartCoroutine(Cooldown(extraSkipCooldownText, extraSkipButton, () => extraSkipReady = true)); } IEnumerator Cooldown(TMP_Text label, Button button, System.Action done) { float t = cooldownDuration; if (label != null) label.gameObject.SetActive(true); while (t > 0) { if (label != null) label.text = Mathf.CeilToInt(t).ToString(); t -= Time.deltaTime; yield return null; } if (label != null) { label.text = ""; label.gameObject.SetActive(false); } if (button != null) button.interactable = true; done?.Invoke(); } void SaveProgressOnly() { PlayerPrefs.SetInt("Mod24Skor", score); PlayerPrefs.SetInt("Mod24Score", score); PlayerPrefs.SetInt("Mod24Correct", correctCount); PlayerPrefs.SetInt("Mod24Wrong", wrongCount); PlayerPrefs.SetInt(modKey + "_LastScore", score); PlayerPrefs.SetInt(modKey + "_Correct", correctCount); PlayerPrefs.SetInt(modKey + "_Wrong", wrongCount); PlayerPrefs.SetInt(modKey + "_LastCombo", combo); PlayerPrefs.SetInt(modKey + "_BestComboRun", bestComboThisRun); PlayerPrefs.SetInt(modKey + "_QuestionNumber", questionNumber); PlayerPrefs.SetInt(modKey + "_LastTimeLeft", Mathf.CeilToInt(timeLeft)); int total = correctCount + wrongCount; int accuracy = total > 0 ? Mathf.RoundToInt((correctCount / (float)total) * 100f) : 0; PlayerPrefs.SetInt(modKey + "_LastAccuracy", accuracy); if (score > PlayerPrefs.GetInt(modKey + "_MaxScore", 0)) PlayerPrefs.SetInt(modKey + "_MaxScore", score); if (score > PlayerPrefs.GetInt("Mod24LeaderScore", 0)) PlayerPrefs.SetInt("Mod24LeaderScore", score); if (score > PlayerPrefs.GetInt("Mod24BestScore", 0)) PlayerPrefs.SetInt("Mod24BestScore", score); if (bestComboThisRun > PlayerPrefs.GetInt(modKey + "_BestCombo", 0)) PlayerPrefs.SetInt(modKey + "_BestCombo", bestComboThisRun); PlayerPrefs.Save(); } void GameOver() { if (gameOver) return; gameOver = true; SaveProgressOnly(); PlayerPrefs.SetString("LastPlayedMod", modKey); PlayerPrefs.SetInt(modKey + "_TotalCorrect", PlayerPrefs.GetInt(modKey + "_TotalCorrect", 0) + correctCount); PlayerPrefs.SetInt(modKey + "_TotalWrong", PlayerPrefs.GetInt(modKey + "_TotalWrong", 0) + wrongCount); PlayerPrefs.SetInt(modKey + "_PlayCount", PlayerPrefs.GetInt(modKey + "_PlayCount", 0) + 1); PlayerPrefs.SetInt("TotalScore", PlayerPrefs.GetInt("TotalScore", 0) + score); PlayerPrefs.Save(); SceneManager.LoadScene(resultSceneName); } void UpdateScoreUI() { if (scoreText != null) scoreText.text = score.ToString(); } void UpdateComboUI() { if (comboText != null) comboText.text = combo + "x"; } void UpdateTimerUI() { if (timerText == null) return; timerText.text = Mathf.CeilToInt(timeLeft).ToString(); if (timeLeft <= 10f) timerText.color = Color.red; else timerText.color = Color.white; } void UpdateStatsUI() { int total = correctCount + wrongCount; int accuracy = total > 0 ? Mathf.RoundToInt((correctCount / (float)total) * 100f) : 0; if (questionCounterText != null) questionCounterText.text = questionNumber + " / " + allQuestions.Count; if (accuracyText != null) accuracyText.text = "%" + accuracy; if (bestComboText != null) bestComboText.text = bestComboThisRun.ToString(); } void ShowFeedback(string msg, Color color) { if (feedbackText == null) return; feedbackText.text = msg; feedbackText.color = FullAlpha(color); StopCoroutine(nameof(FeedbackAnim)); StartCoroutine(nameof(FeedbackAnim)); } float GetComboMultiplier(int c) { if (c >= 8) return mult_8plus; if (c >= 5) return mult_5to7; if (c >= 3) return mult_3to4; return mult_1to2; } IEnumerator FeedbackAnim() { if (feedbackText == null) yield break; Vector3 normal = Vector3.one; Vector3 big = Vector3.one * 1.15f; feedbackText.transform.localScale = normal; float t = 0; while (t < 1) { t += Time.deltaTime / 0.15f; feedbackText.transform.localScale = Vector3.Lerp(normal, big, t); yield return null; } t = 0; while (t < 1) { t += Time.deltaTime / 0.15f; feedbackText.transform.localScale = Vector3.Lerp(big, normal, t); yield return null; } feedbackText.transform.localScale = normal; } IEnumerator ButtonPop(Button btn) { if (btn == null) yield break; Vector3 normal = btn.transform.localScale; Vector3 big = normal * 1.12f; float t = 0; while (t < 1) { t += Time.deltaTime / 0.12f; btn.transform.localScale = Vector3.Lerp(normal, big, t); yield return null; } t = 0; while (t < 1) { t += Time.deltaTime / 0.12f; btn.transform.localScale = Vector3.Lerp(big, normal, t); yield return null; } btn.transform.localScale = normal; } IEnumerator ButtonShake(Button btn) { if (btn == null) yield break; Vector3 original = btn.transform.localPosition; float duration = 0.20f; float elapsed = 0f; float power = 8f; while (elapsed < duration) { elapsed += Time.deltaTime; float x = Mathf.Sin(elapsed * 80f) * power; btn.transform.localPosition = original + new Vector3(x, 0f, 0f); yield return null; } btn.transform.localPosition = original; } IEnumerator MainTextPopAnim() { if (paragraphText == null) yield break; Vector3 normal = Vector3.one; Vector3 small = Vector3.one * 0.92f; paragraphText.transform.localScale = small; if (questionText != null) questionText.transform.localScale = small; float t = 0; while (t < 1) { t += Time.deltaTime / 0.18f; paragraphText.transform.localScale = Vector3.Lerp(small, normal, t); if (questionText != null) questionText.transform.localScale = Vector3.Lerp(small, normal, t); yield return null; } paragraphText.transform.localScale = normal; if (questionText != null) questionText.transform.localScale = normal; } Color FullAlpha(Color c) { c.a = 1f; return c; } void Shuffle(List list) { for (int i = 0; i < list.Count; i++) { int rnd = Random.Range(i, list.Count); T temp = list[i]; list[i] = list[rnd]; list[rnd] = temp; } } }