starOfWords/Assets/Scripts/4.0/game/DialogueComplete22Mod.cs
portakal ecb1c9edea Initial commit: Star of Words Unity project
Snapshot of the existing Unity 6 language-learning word game before any
refactoring. Adds Unity .gitignore and .gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:31:20 +03:00

1223 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.SceneManagement;
using System.Collections;
using System.Collections.Generic;
public class DialogueComplete22Mod : MonoBehaviour
{
[Header("DATA")]
public TextAsset wordFile;
public string wordsResourcePath = "Mod22Words";
[Header("MAIN DIALOGUE TEXT")]
[Tooltip("Ekranda kısa diyalog burada gösterilecek.")]
public TMP_Text dialogueText;
[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 TEXTS")]
public TMP_Text questionCounterText;
public TMP_Text accuracyText;
public TMP_Text streakText;
[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 22";
public string resultSceneName = "modres 22";
[Header("LANGUAGE TEST")]
[Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | QuestionLangID = ekrandaki diyalog dili")]
public int forcedQuestionLanguageIndex = -1;
[Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | AnswerLangID = şıklardaki cevap 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 correctTimeBonus = 2f;
public float wrongTimePenalty = 0f;
public float nextQuestionDelay = 1.05f;
[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, 245);
public Color selectedButtonColor = new Color32(165, 115, 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, 170);
readonly string[] langNames = { "Türkçe", "English", "Español", "Deutsch", "Français", "Português" };
const string modKey = "Mod22";
class DialogueEntry
{
public string[] dialogues = new string[6];
// Her dil için 4 cevap şıkkı:
// 0 doğru
// 1 yanlış
// 2 yanlış
// 3 yanlış
public string[] options = new string[24];
public int sourceLine;
}
readonly List<DialogueEntry> allQuestions = new List<DialogueEntry>();
readonly List<DialogueEntry> questionPool = new List<DialogueEntry>();
DialogueEntry currentQuestion;
int screenLangIndex = 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 (!ValidateRequiredReferences())
return;
if (allQuestions.Count < 1)
{
Debug.LogError("Mod22: Geçerli soru yok. TXT dosyası 30 alanlı satırlardan oluşmalı.");
enabled = false;
return;
}
PrepareUI();
BindButtons();
ResetQuestionPool();
UpdateLanguageUI();
UpdateScoreUI();
UpdateComboUI();
UpdateTimerUI();
UpdateExtraStatsUI();
NextQuestion();
Debug.Log("Mod22 Diyalog Dili: " + screenLangIndex + " / " + langNames[screenLangIndex]);
Debug.Log("Mod22 Şık Dili: " + optionLangIndex + " / " + langNames[optionLangIndex]);
Debug.Log("Mod22 Toplam Soru: " + allQuestions.Count);
}
void Update()
{
if (gameOver)
return;
timeLeft -= Time.deltaTime;
if (timeLeft <= 0f)
{
timeLeft = 0f;
UpdateTimerUI();
GameOver();
return;
}
UpdateTimerUI();
}
bool ValidateRequiredReferences()
{
if (dialogueText == null)
{
Debug.LogError("Mod22: dialogueText atanmadı.");
enabled = false;
return false;
}
if (answerButtons == null || answerButtons.Length < 4)
{
Debug.LogError("Mod22: En az 4 answerButtons atanmalı.");
enabled = false;
return false;
}
if (answerTexts == null || answerTexts.Length < 4)
{
Debug.LogError("Mod22: En az 4 answerTexts atanmalı.");
enabled = false;
return false;
}
for (int i = 0; i < 4; i++)
{
if (answerButtons[i] == null)
{
Debug.LogError("Mod22: answerButtons[" + i + "] boş.");
enabled = false;
return false;
}
if (answerTexts[i] == null)
{
Debug.LogError("Mod22: answerTexts[" + i + "] boş.");
enabled = false;
return false;
}
}
return true;
}
// TXT FORMAT:
// 0-5 diyalog:
// trDialogue|enDialogue|esDialogue|deDialogue|frDialogue|ptDialogue
//
// 6-29 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<TextAsset>(wordsResourcePath);
if (txt == null)
{
Debug.LogError("Mod22: TXT bulunamadı. Şuraya koy: Assets/Resources/" + wordsResourcePath + ".txt");
return;
}
string[] lines = txt.text.Split(
new[] { '\n', '\r' },
System.StringSplitOptions.RemoveEmptyEntries
);
int skipped = 0;
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 < 30)
{
skipped++;
Debug.LogWarning("Mod22 TXT eksik satır atlandı. Satır: " + (i + 1) + " Alan sayısı: " + p.Length + " / Gerekli: 30");
continue;
}
DialogueEntry entry = new DialogueEntry();
entry.sourceLine = i + 1;
bool valid = true;
for (int l = 0; l < 6; l++)
{
entry.dialogues[l] = CleanField(p[l]);
if (string.IsNullOrWhiteSpace(entry.dialogues[l]))
valid = false;
}
for (int q = 0; q < 24; q++)
{
entry.options[q] = CleanField(p[6 + q]);
if (string.IsNullOrWhiteSpace(entry.options[q]))
valid = false;
}
if (!valid)
{
skipped++;
Debug.LogWarning("Mod22 TXT boş alan içerdiği için satır atlandı. Satır: " + (i + 1));
continue;
}
allQuestions.Add(entry);
}
Debug.Log("Mod22 yüklenen soru sayısı: " + allQuestions.Count + " | Atlanan satır: " + skipped);
}
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]", "____")
.Replace(" ", " ");
}
void ResolveLanguages()
{
if (forcedQuestionLanguageIndex >= 0 && forcedQuestionLanguageIndex <= 5)
screenLangIndex = forcedQuestionLanguageIndex;
else
screenLangIndex = PlayerPrefs.GetInt("QuestionLangID", 1);
if (forcedAnswerLanguageIndex >= 0 && forcedAnswerLanguageIndex <= 5)
optionLangIndex = forcedAnswerLanguageIndex;
else
optionLangIndex = PlayerPrefs.GetInt("AnswerLangID", 0);
screenLangIndex = ClampLanguage(screenLangIndex, 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;
screenLangIndex = 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 =
GetText("dialogueLabel").ToUpper() + ": " + langNames[screenLangIndex] +
" | " + GetText("answerLabel").ToUpper() + ": " + langNames[optionLangIndex];
}
if (titleText != null)
titleText.text = GetTitleText();
}
string GetTitleText()
{
switch (optionLangIndex)
{
case 0: return "Eksik Cevabı Tamamla";
case 1: return "Complete the Missing Reply";
case 2: return "Completa la respuesta faltante";
case 3: return "Ergänze die fehlende Antwort";
case 4: return "Complète la réponse manquante";
case 5: return "Complete a resposta que falta";
default: return "Complete the Missing Reply";
}
}
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 == "dialogueLabel")
{
if (screenLangIndex == 0) return "Diyalog";
if (screenLangIndex == 1) return "Dialogue";
if (screenLangIndex == 2) return "Diálogo";
if (screenLangIndex == 3) return "Dialog";
if (screenLangIndex == 4) return "Dialogue";
if (screenLangIndex == 5) return "Diálogo";
}
if (key == "answerLabel")
{
if (lang == 0) return "Cevap";
if (lang == 1) return "Answer";
if (lang == 2) return "Respuesta";
if (lang == 3) return "Antwort";
if (lang == 4) return "Réponse";
if (lang == 5) return "Resposta";
}
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";
}
if (key == "hint")
{
if (lang == 0) return "İPUCU KULLANILDI";
if (lang == 1) return "HINT USED";
if (lang == 2) return "PISTA USADA";
if (lang == 3) return "HINWEIS GENUTZT";
if (lang == 4) return "INDICE UTILISÉ";
if (lang == 5) return "DICA USADA";
}
return key;
}
void PrepareUI()
{
if (dialogueText != null)
{
dialogueText.alignment = TextAlignmentOptions.Center;
dialogueText.textWrappingMode = TextWrappingModes.Normal;
}
if (feedbackText != null)
{
feedbackText.text = "";
feedbackText.alignment = TextAlignmentOptions.Center;
feedbackText.textWrappingMode = TextWrappingModes.Normal;
}
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 = 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 dialogueValue = currentQuestion.dialogues[screenLangIndex];
string correctAnswer = GetOption(currentQuestion, optionLangIndex, 0);
if (dialogueText != null)
dialogueText.text = GetText("dialogueLabel") + ":\n" + dialogueValue;
List<string> options = new List<string>();
for (int i = 0; i < 4; i++)
{
string option = GetOption(currentQuestion, optionLangIndex, i);
if (!string.IsNullOrWhiteSpace(option) && !options.Contains(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[i] != null)
answerTexts[i].text = options[i];
}
if (feedbackText != null)
feedbackText.text = "";
if (submitButton != null)
submitButton.interactable = true;
UpdateExtraStatsUI();
StartCoroutine(DialoguePopAnim());
}
string GetOption(DialogueEntry 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 = normalButtonColor;
if (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].interactable)
answerButtons[i].image.color = normalButtonColor;
answerButtons[i].transform.localScale = originalButtonScales[i];
}
if (answerButtons[index].image != null)
answerButtons[index].image.color = selectedButtonColor;
answerButtons[index].transform.localScale = originalButtonScales[index] * 0.96f;
}
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();
UpdateExtraStatsUI();
SaveProgressOnly();
StartCoroutine(NextQuestionDelay());
}
void CorrectAnswer()
{
if (answerButtons[selectedAnswer].image != null)
answerButtons[selectedAnswer].image.color = 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 = wrongButtonColor;
if (correctAnswerIndex >= 0 && correctAnswerIndex < answerButtons.Length)
{
if (answerButtons[correctAnswerIndex].image != null)
answerButtons[correctAnswerIndex].image.color = 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);
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 = disabledButtonColor;
if (answerTexts != null && i < answerTexts.Length && answerTexts[i] != null)
answerTexts[i].text = "—";
removed++;
}
ShowFeedback(GetText("hint"), Color.white);
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("Mod22Skor", score);
PlayerPrefs.SetInt("Mod22Score", score);
PlayerPrefs.SetInt("Mod22Correct", correctCount);
PlayerPrefs.SetInt("Mod22Wrong", 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 + "_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("Mod22LeaderScore", 0))
PlayerPrefs.SetInt("Mod22LeaderScore", score);
if (score > PlayerPrefs.GetInt("Mod22BestScore", 0))
PlayerPrefs.SetInt("Mod22BestScore", 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 UpdateExtraStatsUI()
{
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 (streakText != null)
streakText.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.10f;
btn.transform.localScale = Vector3.Lerp(normal, big, t);
yield return null;
}
t = 0;
while (t < 1)
{
t += Time.deltaTime / 0.10f;
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 DialoguePopAnim()
{
if (dialogueText == null)
yield break;
Vector3 normal = Vector3.one;
Vector3 small = Vector3.one * 0.92f;
dialogueText.transform.localScale = small;
float t = 0;
while (t < 1)
{
t += Time.deltaTime / 0.16f;
dialogueText.transform.localScale = Vector3.Lerp(small, normal, t);
yield return null;
}
dialogueText.transform.localScale = normal;
}
Color FullAlpha(Color c)
{
c.a = 1f;
return c;
}
void Shuffle<T>(List<T> 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;
}
}
}