starOfWords/Assets/Scripts/4.0/game/QuestionAnswer21Mod.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

1107 lines
No EOL
30 KiB
C#
Raw Permalink 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 QuestionAnswer21Mod : MonoBehaviour
{
[Header("DATA")]
[Tooltip("Format (virgül ayraçlı, 30 alan): trCevap,enCevap,esCevap,deCevap,frCevap,ptCevap, sonra her dil için 4 şık (doğru,yanlış,yanlış,yanlış) tr/en/es/de/fr/pt sırasıyla.")]
public TextAsset wordFile;
public string wordsResourcePath = "Mod21Words";
[Header("MAIN TEXT")]
[Tooltip("Ekranda cevap / ifade / kısa bilgi 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("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 21";
public string resultSceneName = "modres 21";
[Header("LANGUAGE TEST")]
[Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | QuestionLangID = ekrandaki cevap dili")]
public int forcedQuestionLanguageIndex = -1;
[Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | AnswerLangID = şıklardaki soru dili")]
public int forcedAnswerLanguageIndex = -1;
[Header("GAME SETTINGS")]
public float startTime = 60f;
public int basePoints = 50;
public int wrongPenalty = 50;
public float correctTimeBonus = 2f;
public float nextQuestionDelay = 1.15f;
[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[] langCodes = { "tr", "en", "es", "de", "fr", "pt" };
readonly string[] langNames = { "Türkçe", "English", "Español", "Deutsch", "Français", "Português" };
const string modKey = "Mod21";
class QuestionAnswerEntry
{
public string[] answers = new string[6];
// Her dil için 4 soru şıkkı:
// 0 doğru
// 1 yanlış
// 2 yanlış
// 3 yanlış
public string[] options = new string[24];
}
readonly List<QuestionAnswerEntry> allQuestions = new List<QuestionAnswerEntry>();
readonly List<QuestionAnswerEntry> questionPool = new List<QuestionAnswerEntry>();
QuestionAnswerEntry currentQuestion;
// ÖNEMLİ:
// screenLangIndex = ekrandaki cevap / bilgi dili
// optionLangIndex = şıklardaki soru cümlelerinin dili
int screenLangIndex = 0;
int optionLangIndex = 1;
int selectedAnswer = -1;
int correctAnswerIndex = -1;
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 = PlayerPrefs.GetFloat("SelectedGameTime", startTime);
if (timeLeft <= 0f)
timeLeft = startTime;
if (allQuestions.Count < 1)
{
Debug.LogError("Mod21: En az 1 geçerli TXT satırı gerekli. Yüklenen soru sayısı: " + allQuestions.Count);
enabled = false;
return;
}
if (questionText == null)
{
Debug.LogError("Mod21: questionText atanmadı.");
enabled = false;
return;
}
if (answerButtons == null || answerButtons.Length < 4 || answerTexts == null || answerTexts.Length < 4)
{
Debug.LogError("Mod21: 4 answerButtons ve 4 answerTexts atanmalı.");
enabled = false;
return;
}
PrepareUI();
BindButtons();
ResetQuestionPool();
UpdateLanguageUI();
UpdateScoreUI();
UpdateComboUI();
UpdateTimerUI();
NextQuestion();
Debug.Log("Mod21 Ekran Cevap Dili: " + screenLangIndex + " / " + langNames[screenLangIndex]);
Debug.Log("Mod21 Şık Soru Dili: " + optionLangIndex + " / " + langNames[optionLangIndex]);
}
void Update()
{
if (gameOver)
return;
timeLeft -= Time.deltaTime;
if (timeLeft <= 0f)
{
timeLeft = 0f;
UpdateTimerUI();
GameOver();
return;
}
UpdateTimerUI();
}
// TXT FORMAT (virgül ayraçlı, 30 alan):
// 0-5 cevaplar:
// trAnswer,enAnswer,esAnswer,deAnswer,frAnswer,ptAnswer
//
// 6-29 soru şı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("Mod21: TXT bulunamadı. Şuraya koy: Assets/Resources/" + wordsResourcePath + ".txt");
return;
}
string[] lines = txt.text.Split(
new[] { '\n', '\r' },
System.StringSplitOptions.RemoveEmptyEntries
);
// İYİLEŞTİRME: Aynı TR cevabı iki kez havuza koymamak için dedupe.
HashSet<string> seenAnswers = new HashSet<string>();
int skippedShort = 0;
int skippedInvalid = 0;
int skippedDuplicate = 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)
{
skippedShort++;
Debug.LogWarning("Mod21 TXT eksik satır atlandı. Satır: " + (i + 1) + " Alan sayısı: " + p.Length);
continue;
}
QuestionAnswerEntry entry = new QuestionAnswerEntry();
bool valid = true;
for (int l = 0; l < 6; l++)
{
entry.answers[l] = CleanField(p[l]);
if (string.IsNullOrWhiteSpace(entry.answers[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)
{
skippedInvalid++;
continue;
}
string dedupeKey = entry.answers[0].ToLowerInvariant().Trim();
if (seenAnswers.Contains(dedupeKey))
{
skippedDuplicate++;
continue;
}
seenAnswers.Add(dedupeKey);
allQuestions.Add(entry);
}
Debug.Log("Mod21 yüklenen soru sayısı: " + allQuestions.Count +
" (kısa satır: " + skippedShort +
", eksik alan: " + skippedInvalid +
", tekrar: " + skippedDuplicate + ")");
}
string[] SplitLineSmart(string line)
{
// Birincil format artık virgül. Eski pipe/tab dosyaları da bozulmasın diye fallback bırakıldı.
if (line.Contains(","))
return line.Split(',');
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();
}
void ResolveLanguages()
{
// SENİN ModSelectLanguageSettings SCRIPTİNE GÖRE:
// QuestionLangID = ekranda görünen cevap / bilgi dili
// AnswerLangID = şıklardaki soru cümlesi dili
if (forcedQuestionLanguageIndex >= 0 && forcedQuestionLanguageIndex <= 5)
screenLangIndex = forcedQuestionLanguageIndex;
else
screenLangIndex = PlayerPrefs.GetInt("QuestionLangID", 0);
if (forcedAnswerLanguageIndex >= 0 && forcedAnswerLanguageIndex <= 5)
optionLangIndex = forcedAnswerLanguageIndex;
else
optionLangIndex = PlayerPrefs.GetInt("AnswerLangID", 1);
screenLangIndex = ClampLanguage(screenLangIndex, 0);
optionLangIndex = ClampLanguage(optionLangIndex, 1);
}
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;
// QuestionLangID = ekranda görünen cevap / bilgi dili
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;
// AnswerLangID = şıklardaki soru cümlesi dili
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 =
"EKRAN: " + langNames[screenLangIndex] +
" | ŞIKLAR: " + langNames[optionLangIndex];
}
if (titleText != null)
titleText.text = GetTitleText();
}
string GetTitleText()
{
switch (optionLangIndex)
{
case 0: return "Doğru Soru Cümlesini Seç";
case 1: return "Choose the Correct Question";
case 2: return "Elige la pregunta correcta";
case 3: return "Wähle den richtigen Fragesatz";
case 4: return "Choisis la bonne question";
case 5: return "Escolha a pergunta correta";
default: return "Choose the Correct Question";
}
}
string GetText(string key)
{
int lang = optionLangIndex;
if (key == "choose")
{
if (lang == 0) return "BİR SORU SEÇ";
if (lang == 1) return "CHOOSE A QUESTION";
if (lang == 2) return "ELIGE UNA PREGUNTA";
if (lang == 3) return "WÄHLE EINE FRAGE";
if (lang == 4) return "CHOISIS UNE QUESTION";
if (lang == 5) return "ESCOLHA UMA PERGUNTA";
}
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 == "correctQuestion")
{
if (lang == 0) return "Doğru soru";
if (lang == 1) return "Correct question";
if (lang == 2) return "Pregunta correcta";
if (lang == 3) return "Richtige Frage";
if (lang == 4) return "Bonne question";
if (lang == 5) return "Pergunta correta";
}
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 (questionText != null)
{
questionText.alignment = TextAlignmentOptions.Center;
questionText.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 = 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);
RefreshCurrentQuestion();
}
void RefreshCurrentQuestion()
{
if (currentQuestion == null)
return;
waitingNextQuestion = false;
selectedAnswer = -1;
correctAnswerIndex = -1;
ResetButtons();
string answerValue = currentQuestion.answers[screenLangIndex];
string correctQuestion = GetOption(currentQuestion, optionLangIndex, 0);
// İYİLEŞTİRME: Boş label varken baştaki ":" görünmesin.
if (questionText != null)
questionText.text = answerValue;
// İYİLEŞTİRME: Aynı metinli şık varsa doğru cevabı kaybetmemek için
// doğru şıkkı işaretleyerek karıştırıyoruz.
List<KeyValuePair<string, bool>> built = new List<KeyValuePair<string, bool>>();
for (int i = 0; i < 4; i++)
{
string option = GetOption(currentQuestion, optionLangIndex, i);
if (string.IsNullOrWhiteSpace(option))
continue;
built.Add(new KeyValuePair<string, bool>(option, i == 0));
}
while (built.Count < 4)
built.Add(new KeyValuePair<string, bool>("?", false));
Shuffle(built);
correctAnswerIndex = 0;
for (int i = 0; i < 4; i++)
{
if (built[i].Value)
correctAnswerIndex = i;
if (answerTexts != null && i < answerTexts.Length && answerTexts[i] != null)
answerTexts[i].text = built[i].Key;
}
if (feedbackText != null)
feedbackText.text = "";
if (submitButton != null)
submitButton.interactable = true;
StartCoroutine(QuestionPopAnim());
}
string GetOption(QuestionAnswerEntry 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;
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();
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;
string correctText = "";
if (correctAnswerIndex >= 0 && correctAnswerIndex < answerTexts.Length && answerTexts[correctAnswerIndex] != null)
correctText = answerTexts[correctAnswerIndex].text;
ShowFeedback(GetText("wrong") + "\n" + GetText("correctQuestion") + ": " + correctText, wrongButtonColor);
}
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("Mod21Skor", score);
PlayerPrefs.SetInt("Mod21Score", score);
PlayerPrefs.SetInt("Mod21Correct", correctCount);
PlayerPrefs.SetInt("Mod21Wrong", 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("Mod21LeaderScore", 0))
PlayerPrefs.SetInt("Mod21LeaderScore", score);
if (score > PlayerPrefs.GetInt("Mod21BestScore", 0))
PlayerPrefs.SetInt("Mod21BestScore", 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 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 QuestionPopAnim()
{
if (questionText == null)
yield break;
Vector3 normal = Vector3.one;
Vector3 small = Vector3.one * 0.92f;
questionText.transform.localScale = small;
float t = 0;
while (t < 1)
{
t += Time.deltaTime / 0.18f;
questionText.transform.localScale = Vector3.Lerp(small, normal, t);
yield return null;
}
questionText.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;
}
}
}