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>
1109 lines
30 KiB
C#
1109 lines
30 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
using UnityEngine.SceneManagement;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
|
||
public class ParagraphTrueFalse25Mod : MonoBehaviour
|
||
{
|
||
[Header("DATA")]
|
||
public TextAsset wordFile;
|
||
public string wordsResourcePath = "Mod25Words";
|
||
|
||
[Header("MAIN TEXTS")]
|
||
[Tooltip("Paragraf burada gösterilecek.")]
|
||
public TMP_Text paragraphText;
|
||
|
||
[Tooltip("Doğru / yanlış kontrol edilecek ifade burada gösterilecek.")]
|
||
public TMP_Text statementText;
|
||
|
||
[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("TRUE / FALSE BUTTONS")]
|
||
[Tooltip("En az 2 buton bağla. 0 = TRUE, 1 = FALSE. 3. ve 4. varsa otomatik kapatılır.")]
|
||
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 25";
|
||
public string resultSceneName = "modres 25";
|
||
|
||
[Header("LANGUAGE TEST")]
|
||
[Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | QuestionLangID = paragraf ve ifade dili")]
|
||
public int forcedQuestionLanguageIndex = -1;
|
||
|
||
[Tooltip("-1 otomatik. 0 TR, 1 EN, 2 ES, 3 DE, 4 FR, 5 PT | AnswerLangID = TRUE / FALSE yazı 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.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 = "Mod25";
|
||
|
||
class TrueFalseEntry
|
||
{
|
||
public string[] paragraphs = new string[6];
|
||
public string[] statements = new string[6];
|
||
public bool isTrue;
|
||
}
|
||
|
||
readonly List<TrueFalseEntry> allQuestions = new List<TrueFalseEntry>();
|
||
readonly List<TrueFalseEntry> questionPool = new List<TrueFalseEntry>();
|
||
|
||
TrueFalseEntry currentQuestion;
|
||
|
||
int paragraphLangIndex = 1;
|
||
int optionLangIndex = 0;
|
||
|
||
int selectedAnswer = -1;
|
||
int correctAnswerIndex = -1; // 0 = TRUE, 1 = FALSE
|
||
|
||
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("Mod25: En az 1 geçerli TXT satırı gerekli. Yüklenen soru sayısı: " + allQuestions.Count);
|
||
enabled = false;
|
||
return;
|
||
}
|
||
|
||
if (paragraphText == null)
|
||
{
|
||
Debug.LogError("Mod25: paragraphText atanmadı.");
|
||
enabled = false;
|
||
return;
|
||
}
|
||
|
||
if (statementText == null)
|
||
{
|
||
Debug.LogError("Mod25: statementText atanmadı.");
|
||
enabled = false;
|
||
return;
|
||
}
|
||
|
||
if (answerButtons == null || answerButtons.Length < 2 || answerTexts == null || answerTexts.Length < 2)
|
||
{
|
||
Debug.LogError("Mod25: En az 2 answerButtons ve 2 answerTexts atanmalı. 0 TRUE, 1 FALSE.");
|
||
enabled = false;
|
||
return;
|
||
}
|
||
|
||
PrepareUI();
|
||
BindButtons();
|
||
ResetQuestionPool();
|
||
|
||
UpdateLanguageUI();
|
||
UpdateScoreUI();
|
||
UpdateComboUI();
|
||
UpdateTimerUI();
|
||
|
||
NextQuestion();
|
||
|
||
Debug.Log("Mod25 Paragraf Dili: " + paragraphLangIndex + " / " + langNames[paragraphLangIndex]);
|
||
Debug.Log("Mod25 TRUE/FALSE 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 ifade:
|
||
// trStatement|enStatement|esStatement|deStatement|frStatement|ptStatement
|
||
//
|
||
// 12 cevap:
|
||
// true veya false
|
||
|
||
void LoadQuestions()
|
||
{
|
||
allQuestions.Clear();
|
||
|
||
TextAsset txt = wordFile;
|
||
|
||
if (txt == null)
|
||
txt = Resources.Load<TextAsset>(wordsResourcePath);
|
||
|
||
if (txt == null)
|
||
{
|
||
Debug.LogError("Mod25: 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 < 13)
|
||
{
|
||
Debug.LogWarning("Mod25 TXT eksik satır atlandı. Satır: " + (i + 1) + " Alan sayısı: " + p.Length);
|
||
continue;
|
||
}
|
||
|
||
TrueFalseEntry entry = new TrueFalseEntry();
|
||
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.statements[l] = CleanField(p[6 + l]);
|
||
|
||
if (string.IsNullOrWhiteSpace(entry.statements[l]))
|
||
valid = false;
|
||
}
|
||
|
||
string answer = CleanField(p[12]).ToLowerInvariant();
|
||
|
||
if (answer == "true" || answer == "1" || answer == "yes" || answer == "doğru" || answer == "dogru")
|
||
entry.isTrue = true;
|
||
else if (answer == "false" || answer == "0" || answer == "no" || answer == "yanlış" || answer == "yanlis")
|
||
entry.isTrue = false;
|
||
else
|
||
{
|
||
Debug.LogWarning("Mod25 TXT cevap alanı true/false değil. Satır: " + (i + 1) + " Cevap: " + p[12]);
|
||
valid = false;
|
||
}
|
||
|
||
if (valid)
|
||
allQuestions.Add(entry);
|
||
}
|
||
|
||
Debug.Log("Mod25 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 ifade dili
|
||
// AnswerLangID = TRUE / FALSE buton yazılarının 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] +
|
||
" | SEÇİM: " + langNames[optionLangIndex];
|
||
}
|
||
|
||
if (titleText != null)
|
||
titleText.text = GetTitleText();
|
||
}
|
||
|
||
string GetTitleText()
|
||
{
|
||
switch (optionLangIndex)
|
||
{
|
||
case 0: return "Doğru mu Yanlış mı?";
|
||
case 1: return "True or False?";
|
||
case 2: return "¿Verdadero o falso?";
|
||
case 3: return "Richtig oder falsch?";
|
||
case 4: return "Vrai ou faux ?";
|
||
case 5: return "Verdadeiro ou falso?";
|
||
default: return "True or False?";
|
||
}
|
||
}
|
||
|
||
string GetText(string key)
|
||
{
|
||
int lang = optionLangIndex;
|
||
|
||
if (key == "choose")
|
||
{
|
||
if (lang == 0) return "DOĞRU VEYA YANLIŞ SEÇ";
|
||
if (lang == 1) return "CHOOSE TRUE OR FALSE";
|
||
if (lang == 2) return "ELIGE VERDADERO O FALSO";
|
||
if (lang == 3) return "WÄHLE RICHTIG ODER FALSCH";
|
||
if (lang == 4) return "CHOISIS VRAI OU FAUX";
|
||
if (lang == 5) return "ESCOLHA VERDADEIRO OU FALSO";
|
||
}
|
||
|
||
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 == "trueButton")
|
||
{
|
||
if (lang == 0) return "DOĞRU";
|
||
if (lang == 1) return "TRUE";
|
||
if (lang == 2) return "VERDADERO";
|
||
if (lang == 3) return "RICHTIG";
|
||
if (lang == 4) return "VRAI";
|
||
if (lang == 5) return "VERDADEIRO";
|
||
}
|
||
|
||
if (key == "falseButton")
|
||
{
|
||
if (lang == 0) return "YANLIŞ";
|
||
if (lang == 1) return "FALSE";
|
||
if (lang == 2) return "FALSO";
|
||
if (lang == 3) return "FALSCH";
|
||
if (lang == 4) return "FAUX";
|
||
if (lang == 5) return "FALSO";
|
||
}
|
||
|
||
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 == "statementLabel")
|
||
{
|
||
if (paragraphLangIndex == 0) return "İfade";
|
||
if (paragraphLangIndex == 1) return "Statement";
|
||
if (paragraphLangIndex == 2) return "Afirmación";
|
||
if (paragraphLangIndex == 3) return "Aussage";
|
||
if (paragraphLangIndex == 4) return "Phrase";
|
||
if (paragraphLangIndex == 5) return "Afirmação";
|
||
}
|
||
|
||
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.textWrappingMode = TextWrappingModes.Normal;
|
||
}
|
||
|
||
if (statementText != null)
|
||
{
|
||
statementText.alignment = TextAlignmentOptions.Center;
|
||
statementText.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);
|
||
|
||
if (i > 1)
|
||
answerButtons[i].gameObject.SetActive(false);
|
||
}
|
||
|
||
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 = currentQuestion.isTrue ? 0 : 1;
|
||
|
||
ResetButtons();
|
||
|
||
if (paragraphText != null)
|
||
paragraphText.text = GetText("paragraphLabel") + ":\n" + currentQuestion.paragraphs[paragraphLangIndex];
|
||
|
||
if (statementText != null)
|
||
statementText.text = GetText("statementLabel") + ":\n" + currentQuestion.statements[paragraphLangIndex];
|
||
|
||
if (answerTexts != null && answerTexts.Length > 0 && answerTexts[0] != null)
|
||
answerTexts[0].text = GetText("trueButton");
|
||
|
||
if (answerTexts != null && answerTexts.Length > 1 && answerTexts[1] != null)
|
||
answerTexts[1].text = GetText("falseButton");
|
||
|
||
for (int i = 2; i < answerTexts.Length; i++)
|
||
{
|
||
if (answerTexts[i] != null)
|
||
answerTexts[i].text = "";
|
||
}
|
||
|
||
if (feedbackText != null)
|
||
feedbackText.text = "";
|
||
|
||
if (submitButton != null)
|
||
submitButton.interactable = true;
|
||
|
||
StartCoroutine(MainTextPopAnim());
|
||
}
|
||
|
||
void ResetButtons()
|
||
{
|
||
for (int i = 0; i < answerButtons.Length; i++)
|
||
{
|
||
if (answerButtons[i] == null)
|
||
continue;
|
||
|
||
bool active = i < 2;
|
||
answerButtons[i].gameObject.SetActive(active);
|
||
|
||
if (!active)
|
||
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);
|
||
}
|
||
}
|
||
|
||
void SelectAnswer(int index)
|
||
{
|
||
if (gameOver || waitingNextQuestion)
|
||
return;
|
||
|
||
if (index < 0 || index > 1)
|
||
return;
|
||
|
||
selectedAnswer = index;
|
||
|
||
for (int i = 0; i < 2; 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 < 2; 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 < 2)
|
||
{
|
||
if (answerButtons[correctAnswerIndex].image != null)
|
||
answerButtons[correctAnswerIndex].image.color = FullAlpha(correctButtonColor);
|
||
}
|
||
|
||
wrongCount++;
|
||
combo = 0;
|
||
|
||
score -= wrongPenalty;
|
||
|
||
if (score < 0)
|
||
score = 0;
|
||
|
||
string correctText = correctAnswerIndex == 0 ? GetText("trueButton") : GetText("falseButton");
|
||
|
||
ShowFeedback(GetText("wrong") + "\n" + GetText("correctAnswer") + ": " + 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 wrongIndex = correctAnswerIndex == 0 ? 1 : 0;
|
||
|
||
if (wrongIndex == selectedAnswer)
|
||
selectedAnswer = -1;
|
||
|
||
if (answerButtons[wrongIndex] != null)
|
||
{
|
||
answerButtons[wrongIndex].interactable = false;
|
||
|
||
if (answerButtons[wrongIndex].image != null)
|
||
answerButtons[wrongIndex].image.color = FullAlpha(disabledButtonColor);
|
||
|
||
if (answerTexts != null && wrongIndex < answerTexts.Length && answerTexts[wrongIndex] != null)
|
||
answerTexts[wrongIndex].text = "—";
|
||
}
|
||
|
||
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("Mod25Skor", score);
|
||
PlayerPrefs.SetInt("Mod25Score", score);
|
||
PlayerPrefs.SetInt("Mod25Correct", correctCount);
|
||
PlayerPrefs.SetInt("Mod25Wrong", 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("Mod25LeaderScore", 0))
|
||
PlayerPrefs.SetInt("Mod25LeaderScore", score);
|
||
|
||
if (score > PlayerPrefs.GetInt("Mod25BestScore", 0))
|
||
PlayerPrefs.SetInt("Mod25BestScore", 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 MainTextPopAnim()
|
||
{
|
||
if (paragraphText == null)
|
||
yield break;
|
||
|
||
Vector3 normal = Vector3.one;
|
||
Vector3 small = Vector3.one * 0.92f;
|
||
|
||
paragraphText.transform.localScale = small;
|
||
|
||
if (statementText != null)
|
||
statementText.transform.localScale = small;
|
||
|
||
float t = 0;
|
||
|
||
while (t < 1)
|
||
{
|
||
t += Time.deltaTime / 0.18f;
|
||
paragraphText.transform.localScale = Vector3.Lerp(small, normal, t);
|
||
|
||
if (statementText != null)
|
||
statementText.transform.localScale = Vector3.Lerp(small, normal, t);
|
||
|
||
yield return null;
|
||
}
|
||
|
||
paragraphText.transform.localScale = normal;
|
||
|
||
if (statementText != null)
|
||
statementText.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;
|
||
}
|
||
}
|
||
}
|