starOfWords/Assets/Scripts/3.0/mod7/TetrisTranslationDropMod.cs

1118 lines
30 KiB
C#
Raw Permalink Normal View History

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class TetrisTranslationDropMod : MonoBehaviour
{
[Header("━━━ FALLING BLOCK ━━━━━━━━━━━━━━━━━━━━━")]
public RectTransform fallingBlock;
public TMP_Text fallingText;
[Header("━━━ CONTROLS ━━━━━━━━━━━━━━━━━━━━━━━━━")]
public Button btnLeft;
public Button btnRight;
public Button btnDown;
public Button backButton;
[Header("━━━ UI ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")]
public TMP_Text scoreText;
public TMP_Text comboText;
public TMP_Text timerText;
public TMP_Text feedbackText;
[Header("━━━ TRANSLATION SLOTS (4) ━━━━━━━━━━━━━")]
public RectTransform[] translationSlots;
public TMP_Text[] translationSlotTexts;
[Header("━━━ WORD TXT ━━━━━━━━━━━━━━━━━━━━━━━━━")]
[Tooltip("Each line: tr,en,es,de,fr,pt")]
public TextAsset wordsTxt;
[Header("━━━ SCENE SETTINGS ━━━━━━━━━━━━━━━━━━━")]
public string backSceneName = "ModSec 7";
public string resultSceneName = "Mod7Resume";
[Header("━━━ GAME SETTINGS ━━━━━━━━━━━━━━━━━━━")]
public float baseFallSpeed = 220f;
public float maxFallSpeed = 620f;
public float fallSpeedIncreaseRate = 12f;
public float moveStep = 140f;
public float fastFallStep = 350f;
public float gameTime = 90f;
public float nextWordDelay = 0.75f;
public int correctBaseScore = 50;
public int comboBonus = 10;
public int wrongPenalty = 20;
[Header("━━━ SCREEN LIMITS ━━━━━━━━━━━━━━━━━━━━")]
public float minX = -420f;
public float maxX = 420f;
public float spawnY = 650f;
public float bottomLimitY = -650f;
[Header("━━━ COLORS ━━━━━━━━━━━━━━━━━━━━━━━━━━")]
public Color normalSlotColor = new Color32(255, 255, 255, 255);
public Color correctSlotColor = new Color32(70, 220, 120, 255);
public Color wrongSlotColor = new Color32(240, 80, 80, 255);
public Color feedbackCorrectColor = new Color32(70, 220, 120, 255);
public Color feedbackWrongColor = new Color32(240, 80, 80, 255);
public Color timerNormalColor = new Color32(255, 255, 255, 255);
public Color timerWarningColor = new Color32(255, 80, 80, 255);
[Header("━━━ ANIMATION SETTINGS ━━━━━━━━━━━━━━━")]
public float blockSpawnScale = 0.82f;
public float blockSpawnAnimTime = 0.18f;
public float slotPulseScale = 1.08f;
public float slotPulseTime = 0.12f;
public float shakeAmount = 12f;
public float shakeTime = 0.28f;
[Header("━━━ SOUND ━━━━━━━━━━━━━━━━━━━━━━━━━━━")]
public AudioSource sfxSource;
public AudioClip clickClip;
public AudioClip correctClip;
public AudioClip wrongClip;
// INTERNAL
private List<WordEntry> fullDeck = new List<WordEntry>();
private int deckIndex = 0;
private WordEntry currentWord;
private string correctAnswer = "";
private string lastWordKey = "";
private float timer;
private float currentFallSpeed;
private float smoothVelocity = 0f;
private bool processing = false;
private bool gameOver = false;
private int score = 0;
private int combo = 0;
private int bestComboThisRun = 0;
private int correctCount = 0;
private int wrongCount = 0;
private string questionLangCode;
private string answerLangCode = "en";
private string uiLangCode;
private Vector3[] originalSlotScales;
private Vector2[] originalSlotPositions;
private Vector3 originalBlockScale = Vector3.one;
private Coroutine blockSpawnCo;
private Coroutine blockReactCo;
private Coroutine feedbackCo;
private Coroutine[] slotCoroutines;
private string modKey = "Mod7";
private class WordEntry
{
public string tr;
public string en;
public string es;
public string de;
public string fr;
public string pt;
public string Key => tr + "_" + en;
}
// ======================================================
void Start()
{
gameOver = false;
processing = false;
questionLangCode = GetQuestionLanguage();
answerLangCode = "en";
uiLangCode = GetUILanguage();
PlayerPrefs.SetString("QuestionLangCode", questionLangCode);
PlayerPrefs.SetString("AnswerLangCode", answerLangCode);
PlayerPrefs.Save();
timer = PlayerPrefs.GetFloat("SelectedGameTime", gameTime);
if (timer <= 0)
timer = gameTime;
currentFallSpeed = baseFallSpeed;
CacheSlotData();
LoadWords();
if (fullDeck.Count < 4)
{
Debug.LogError("Mod7 kelime dosyası en az 4 satır olmalı! Format: tr,en,es,de,fr,pt");
enabled = false;
return;
}
if (!ValidateUI())
{
enabled = false;
return;
}
BindButtons();
Shuffle(fullDeck);
UpdateUI();
UpdateTimerUI();
SaveProgress();
SpawnNewWord();
}
// ======================================================
void Update()
{
if (gameOver || processing) return;
timer -= Time.deltaTime;
if (timer <= 0)
{
timer = 0;
UpdateTimerUI();
GameOver();
return;
}
UpdateTimerUI();
float targetSpeed = baseFallSpeed + (score / 250f) * fallSpeedIncreaseRate;
targetSpeed = Mathf.Clamp(targetSpeed, baseFallSpeed, maxFallSpeed);
currentFallSpeed = Mathf.SmoothDamp(
currentFallSpeed,
targetSpeed,
ref smoothVelocity,
0.18f
);
if (fallingBlock != null)
fallingBlock.anchoredPosition += Vector2.down * currentFallSpeed * Time.deltaTime;
CheckCollision();
}
// ======================================================
bool ValidateUI()
{
if (fallingBlock == null)
{
Debug.LogError("fallingBlock atanmadı!");
return false;
}
if (fallingText == null)
{
Debug.LogError("fallingText atanmadı!");
return false;
}
if (translationSlots == null || translationSlots.Length < 4)
{
Debug.LogError("translationSlots en az 4 olmalı!");
return false;
}
if (translationSlotTexts == null || translationSlotTexts.Length < 4)
{
Debug.LogError("translationSlotTexts en az 4 olmalı!");
return false;
}
return true;
}
void BindButtons()
{
if (btnLeft != null)
{
btnLeft.onClick.RemoveAllListeners();
btnLeft.onClick.AddListener(() =>
{
PlayClick();
Move(-moveStep);
});
}
if (btnRight != null)
{
btnRight.onClick.RemoveAllListeners();
btnRight.onClick.AddListener(() =>
{
PlayClick();
Move(+moveStep);
});
}
if (btnDown != null)
{
btnDown.onClick.RemoveAllListeners();
btnDown.onClick.AddListener(() =>
{
PlayClick();
FastFall();
});
}
if (backButton != null)
{
backButton.onClick.RemoveAllListeners();
backButton.onClick.AddListener(() =>
{
PlayClick();
SaveProgress();
GoBack();
});
}
}
void GoBack()
{
if (!string.IsNullOrEmpty(backSceneName))
SceneManager.LoadScene(backSceneName);
else
Debug.LogWarning("backSceneName boş!");
}
// ======================================================
void CacheSlotData()
{
if (fallingBlock != null)
originalBlockScale = fallingBlock.localScale;
if (translationSlots == null) return;
originalSlotScales = new Vector3[translationSlots.Length];
originalSlotPositions = new Vector2[translationSlots.Length];
slotCoroutines = new Coroutine[translationSlots.Length];
for (int i = 0; i < translationSlots.Length; i++)
{
if (translationSlots[i] == null) continue;
originalSlotScales[i] = translationSlots[i].localScale;
originalSlotPositions[i] = translationSlots[i].anchoredPosition;
}
}
void ResetSlotVisuals()
{
if (translationSlots == null) return;
for (int i = 0; i < translationSlots.Length; i++)
{
if (translationSlots[i] == null) continue;
if (slotCoroutines != null && i < slotCoroutines.Length && slotCoroutines[i] != null)
{
StopCoroutine(slotCoroutines[i]);
slotCoroutines[i] = null;
}
Image img = translationSlots[i].GetComponent<Image>();
if (img != null)
img.color = FullAlpha(normalSlotColor);
if (originalSlotScales != null && i < originalSlotScales.Length)
translationSlots[i].localScale = originalSlotScales[i];
if (originalSlotPositions != null && i < originalSlotPositions.Length)
translationSlots[i].anchoredPosition = originalSlotPositions[i];
}
}
// ======================================================
void LoadWords()
{
fullDeck.Clear();
if (wordsTxt == null)
{
Debug.LogError("wordsTxt atanmadı!");
return;
}
string[] lines = wordsTxt.text.Split('\n');
foreach (string rawLine in lines)
{
string line = rawLine.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
string[] p = line.Split(',');
if (p.Length < 6)
{
Debug.LogWarning("Eksik satır: " + line);
continue;
}
fullDeck.Add(new WordEntry
{
tr = p[0].Trim(),
en = p[1].Trim(),
es = p[2].Trim(),
de = p[3].Trim(),
fr = p[4].Trim(),
pt = p[5].Trim()
});
}
}
// ======================================================
void SpawnNewWord()
{
if (gameOver) return;
processing = false;
ResetSlotVisuals();
if (feedbackText != null)
feedbackText.text = "";
if (deckIndex >= fullDeck.Count)
{
Shuffle(fullDeck);
deckIndex = 0;
lastWordKey = "";
}
currentWord = fullDeck[deckIndex];
if (currentWord.Key == lastWordKey && fullDeck.Count > 1)
{
int rnd = Random.Range(0, fullDeck.Count);
currentWord = fullDeck[rnd];
deckIndex = rnd + 1;
if (deckIndex >= fullDeck.Count)
deckIndex = 0;
}
else
{
deckIndex++;
}
lastWordKey = currentWord.Key;
string questionWord = GetWordByLang(currentWord, questionLangCode);
correctAnswer = GetWordByLang(currentWord, answerLangCode);
// ✅ Yukarıdan soru dili gelir
fallingText.text = questionWord;
if (fallingBlock != null)
{
fallingBlock.anchoredPosition = new Vector2(0, spawnY);
fallingBlock.localScale = originalBlockScale;
}
// ✅ Alttaki seçenekler İngilizce gelir
SetupTranslationSlots(correctAnswer);
if (blockSpawnCo != null)
StopCoroutine(blockSpawnCo);
if (blockReactCo != null)
StopCoroutine(blockReactCo);
blockSpawnCo = StartCoroutine(BlockSpawnAnim());
}
void SetupTranslationSlots(string correct)
{
List<string> choices = new List<string>();
choices.Add(correct);
int guard = 0;
while (choices.Count < 4 && guard < 500)
{
guard++;
WordEntry randomWord = fullDeck[Random.Range(0, fullDeck.Count)];
string candidate = GetWordByLang(randomWord, answerLangCode);
if (!string.IsNullOrWhiteSpace(candidate) && !choices.Contains(candidate))
choices.Add(candidate);
}
while (choices.Count < 4)
choices.Add("-");
Shuffle(choices);
for (int i = 0; i < 4; i++)
{
if (translationSlotTexts[i] != null)
translationSlotTexts[i].text = choices[i];
if (translationSlots[i] != null)
{
translationSlots[i].name = choices[i];
Image img = translationSlots[i].GetComponent<Image>();
if (img != null)
img.color = FullAlpha(normalSlotColor);
}
}
}
// ======================================================
void Move(float x)
{
if (processing || gameOver || fallingBlock == null) return;
Vector2 pos = fallingBlock.anchoredPosition;
pos.x = Mathf.Clamp(pos.x + x, minX, maxX);
fallingBlock.anchoredPosition = pos;
}
void FastFall()
{
if (processing || gameOver || fallingBlock == null) return;
fallingBlock.anchoredPosition += Vector2.down * fastFallStep;
CheckCollision();
}
// ======================================================
void CheckCollision()
{
if (processing || gameOver) return;
if (fallingBlock == null || translationSlots == null || translationSlots.Length < 4) return;
float slotY = translationSlots[0].anchoredPosition.y;
float blockY = fallingBlock.anchoredPosition.y;
float halfH = fallingBlock.rect.height * 0.5f;
if (blockY - halfH <= slotY)
{
int idx = DetectSlot(fallingBlock.anchoredPosition.x);
if (idx == -1)
{
Wrong("miss");
return;
}
Evaluate(idx);
return;
}
if (blockY + halfH < bottomLimitY)
Wrong("out");
}
int DetectSlot(float x)
{
for (int i = 0; i < 4; i++)
{
if (translationSlots[i] == null) continue;
float sx = translationSlots[i].anchoredPosition.x;
float half = translationSlots[i].rect.width * 0.5f;
if (x >= sx - half && x <= sx + half)
return i;
}
return -1;
}
// ======================================================
void Evaluate(int slotIndex)
{
if (processing || gameOver) return;
processing = true;
string selectedAnswer = "";
if (translationSlotTexts[slotIndex] != null)
selectedAnswer = translationSlotTexts[slotIndex].text;
bool correct = selectedAnswer == correctAnswer;
if (correct)
HandleCorrect(slotIndex);
else
HandleWrong(slotIndex);
UpdateUI();
SaveProgress();
Invoke(nameof(SpawnNewWord), nextWordDelay);
}
void HandleCorrect(int slotIndex)
{
score += correctBaseScore + combo * comboBonus;
combo++;
correctCount++;
if (combo > bestComboThisRun)
bestComboThisRun = combo;
Image img = translationSlots[slotIndex].GetComponent<Image>();
if (img != null)
img.color = FullAlpha(correctSlotColor);
PlayCorrect();
SetFeedback(LocalizeFeedback("correct") + " +" + correctBaseScore, feedbackCorrectColor);
StartSlotAnim(slotIndex, SlotPulse(slotIndex));
if (blockReactCo != null)
StopCoroutine(blockReactCo);
blockReactCo = StartCoroutine(BlockCorrectAnim());
}
void HandleWrong(int slotIndex)
{
score = Mathf.Max(0, score - wrongPenalty);
combo = 0;
wrongCount++;
Image selectedImg = translationSlots[slotIndex].GetComponent<Image>();
if (selectedImg != null)
selectedImg.color = FullAlpha(wrongSlotColor);
for (int i = 0; i < 4; i++)
{
if (translationSlotTexts[i] == null || translationSlots[i] == null)
continue;
if (translationSlotTexts[i].text == correctAnswer)
{
Image correctImg = translationSlots[i].GetComponent<Image>();
if (correctImg != null)
correctImg.color = FullAlpha(correctSlotColor);
StartSlotAnim(i, SlotPulse(i));
}
}
PlayWrong();
SetFeedback(LocalizeFeedback("wrong") + " | " + correctAnswer, feedbackWrongColor);
StartSlotAnim(slotIndex, ShakeSlot(slotIndex));
if (blockReactCo != null)
StopCoroutine(blockReactCo);
blockReactCo = StartCoroutine(BlockWrongAnim());
}
void Wrong(string reason)
{
if (processing || gameOver) return;
processing = true;
score = Mathf.Max(0, score - wrongPenalty);
combo = 0;
wrongCount++;
PlayWrong();
SetFeedback(LocalizeFeedback(reason), feedbackWrongColor);
if (blockReactCo != null)
StopCoroutine(blockReactCo);
blockReactCo = StartCoroutine(BlockWrongAnim());
UpdateUI();
SaveProgress();
Invoke(nameof(SpawnNewWord), nextWordDelay);
}
// ======================================================
void UpdateUI()
{
if (scoreText != null)
scoreText.text = score.ToString();
if (comboText != null)
comboText.text = "x" + combo;
}
void UpdateTimerUI()
{
if (timerText == null) return;
timerText.text = Mathf.CeilToInt(timer).ToString();
if (timer <= 10f)
{
timerText.color = FullAlpha(Color.Lerp(timerWarningColor, Color.white, Mathf.PingPong(Time.time * 3f, 1f)));
timerText.transform.localScale = Vector3.one * (1f + Mathf.Sin(Time.time * 10f) * 0.05f);
}
else
{
timerText.color = FullAlpha(timerNormalColor);
timerText.transform.localScale = Vector3.one;
}
}
void SetFeedback(string msg, Color c)
{
if (feedbackText == null) return;
feedbackText.text = msg;
feedbackText.color = FullAlpha(c);
if (feedbackCo != null)
StopCoroutine(feedbackCo);
feedbackCo = StartCoroutine(FeedbackPop());
}
IEnumerator FeedbackPop()
{
if (feedbackText == null) yield break;
Vector3 normal = Vector3.one;
Vector3 big = Vector3.one * 1.14f;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
feedbackText.transform.localScale = Vector3.Lerp(normal, big, EaseOutBack(t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
feedbackText.transform.localScale = Vector3.Lerp(big, normal, EaseOutQuad(t));
yield return null;
}
feedbackText.transform.localScale = normal;
feedbackCo = null;
}
// ======================================================
void SaveProgress()
{
PlayerPrefs.SetInt("Mod7Skor", score);
PlayerPrefs.SetInt("Mod7Score", score);
PlayerPrefs.SetInt("Mod7_LastScore", score);
PlayerPrefs.SetInt("Mod7_Correct", correctCount);
PlayerPrefs.SetInt("Mod7_Wrong", wrongCount);
PlayerPrefs.SetInt("Mod7_LastCombo", combo);
PlayerPrefs.SetInt("Mod7_BestComboRun", bestComboThisRun);
PlayerPrefs.SetInt("Mod7_LastTimeLeft", Mathf.CeilToInt(timer));
int totalAnswers = correctCount + wrongCount;
int accuracy = totalAnswers > 0 ? Mathf.RoundToInt((correctCount / (float)totalAnswers) * 100f) : 0;
PlayerPrefs.SetInt("Mod7_LastAccuracy", accuracy);
int oldMax = PlayerPrefs.GetInt("Mod7_MaxScore", 0);
if (score > oldMax)
PlayerPrefs.SetInt("Mod7_MaxScore", score);
int oldBestCombo = PlayerPrefs.GetInt("Mod7_BestCombo", 0);
if (bestComboThisRun > oldBestCombo)
PlayerPrefs.SetInt("Mod7_BestCombo", bestComboThisRun);
PlayerPrefs.Save();
}
void GameOver()
{
if (gameOver) return;
gameOver = true;
processing = true;
SaveProgress();
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();
if (!string.IsNullOrEmpty(resultSceneName))
SceneManager.LoadScene(resultSceneName);
else
Debug.LogWarning("resultSceneName boş!");
}
// ======================================================
void StartSlotAnim(int slotIndex, IEnumerator routine)
{
if (slotCoroutines == null) return;
if (slotIndex < 0 || slotIndex >= slotCoroutines.Length) return;
if (slotCoroutines[slotIndex] != null)
StopCoroutine(slotCoroutines[slotIndex]);
slotCoroutines[slotIndex] = StartCoroutine(routine);
}
IEnumerator SlotPulse(int slotIndex)
{
if (translationSlots == null) yield break;
if (slotIndex < 0 || slotIndex >= translationSlots.Length) yield break;
if (translationSlots[slotIndex] == null) yield break;
Vector3 normal = originalSlotScales[slotIndex];
Vector3 big = normal * slotPulseScale;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / Mathf.Max(0.01f, slotPulseTime);
translationSlots[slotIndex].localScale = Vector3.Lerp(normal, big, EaseOutQuad(t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / Mathf.Max(0.01f, slotPulseTime);
translationSlots[slotIndex].localScale = Vector3.Lerp(big, normal, EaseOutQuad(t));
yield return null;
}
translationSlots[slotIndex].localScale = normal;
slotCoroutines[slotIndex] = null;
}
IEnumerator ShakeSlot(int slotIndex)
{
if (translationSlots == null) yield break;
if (slotIndex < 0 || slotIndex >= translationSlots.Length) yield break;
if (translationSlots[slotIndex] == null) yield break;
Vector2 origin = originalSlotPositions[slotIndex];
float elapsed = 0f;
while (elapsed < shakeTime)
{
float power = 1f - elapsed / shakeTime;
float x = Random.Range(-1f, 1f) * shakeAmount * power;
translationSlots[slotIndex].anchoredPosition = origin + new Vector2(x, 0f);
elapsed += Time.deltaTime;
yield return null;
}
translationSlots[slotIndex].anchoredPosition = origin;
slotCoroutines[slotIndex] = null;
}
IEnumerator BlockSpawnAnim()
{
if (fallingBlock == null) yield break;
Vector3 target = originalBlockScale;
Vector3 small = originalBlockScale * blockSpawnScale;
fallingBlock.localScale = small;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / Mathf.Max(0.01f, blockSpawnAnimTime);
fallingBlock.localScale = Vector3.Lerp(small, target, EaseOutBack(t));
yield return null;
}
fallingBlock.localScale = target;
}
IEnumerator BlockCorrectAnim()
{
if (fallingBlock == null) yield break;
Vector3 normal = originalBlockScale;
Vector3 big = originalBlockScale * 1.08f;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.10f;
fallingBlock.localScale = Vector3.Lerp(normal, big, EaseOutQuad(t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.10f;
fallingBlock.localScale = Vector3.Lerp(big, normal, EaseOutQuad(t));
yield return null;
}
fallingBlock.localScale = normal;
}
IEnumerator BlockWrongAnim()
{
if (fallingBlock == null) yield break;
Vector2 origin = fallingBlock.anchoredPosition;
float elapsed = 0f;
float duration = 0.28f;
while (elapsed < duration)
{
float power = 1f - elapsed / duration;
float x = Random.Range(-1f, 1f) * 14f * power;
fallingBlock.anchoredPosition = origin + new Vector2(x, 0f);
elapsed += Time.deltaTime;
yield return null;
}
fallingBlock.anchoredPosition = origin;
}
// ======================================================
void PlayClick()
{
if (sfxSource != null && clickClip != null)
sfxSource.PlayOneShot(clickClip);
}
void PlayCorrect()
{
if (sfxSource != null && correctClip != null)
sfxSource.PlayOneShot(correctClip);
}
void PlayWrong()
{
if (sfxSource != null && wrongClip != null)
sfxSource.PlayOneShot(wrongClip);
}
// ======================================================
string GetQuestionLanguage()
{
string q = PlayerPrefs.GetString("QuestionLangCode", "");
q = NormalizeLang(q);
if (!string.IsNullOrWhiteSpace(q) && q != "en")
return q;
int appId = PlayerPrefs.GetInt("AppLanguage", -1);
if (appId >= 0)
{
string appLang = LangIdToCode(appId);
if (appLang != "en")
return appLang;
}
string appString = NormalizeLang(PlayerPrefs.GetString("AppLanguage", "tr"));
if (appString != "en")
return appString;
return "tr";
}
string GetUILanguage()
{
int appId = PlayerPrefs.GetInt("AppLanguage", -1);
if (appId >= 0)
return LangIdToCode(appId);
return NormalizeLang(PlayerPrefs.GetString("AppLanguage", "en"));
}
string LangIdToCode(int id)
{
return id switch
{
0 => "tr",
2 => "es",
3 => "de",
4 => "fr",
5 => "pt",
_ => "en"
};
}
string NormalizeLang(string raw)
{
if (string.IsNullOrEmpty(raw))
return "en";
raw = raw.Trim().ToLower();
if (raw == "tr" || raw == "turkish" || raw == "tr-tr")
return "tr";
if (raw == "en" || raw == "english" || raw.StartsWith("en-"))
return "en";
if (raw == "es" || raw == "spanish" || raw == "es-es")
return "es";
if (raw == "de" || raw == "german" || raw == "de-de")
return "de";
if (raw == "fr" || raw == "french" || raw == "fr-fr")
return "fr";
if (raw == "pt" || raw == "portuguese" || raw == "pt-pt" || raw == "pt-br")
return "pt";
return "en";
}
string GetWordByLang(WordEntry w, string lang)
{
return lang switch
{
"tr" => w.tr,
"es" => w.es,
"de" => w.de,
"fr" => w.fr,
"pt" => w.pt,
_ => w.en
};
}
string LocalizeFeedback(string key)
{
return key switch
{
"correct" => uiLangCode switch
{
"tr" => "Doğru!",
"es" => "¡Correcto!",
"de" => "Richtig!",
"fr" => "Correct!",
"pt" => "Correto!",
_ => "Correct!"
},
"wrong" => uiLangCode switch
{
"tr" => "Yanlış!",
"es" => "¡Incorrecto!",
"de" => "Falsch!",
"fr" => "Faux!",
"pt" => "Errado!",
_ => "Wrong!"
},
"miss" => uiLangCode switch
{
"tr" => "Kaçırdın!",
"es" => "Fallaste!",
"de" => "Verpasst!",
"fr" => "Raté!",
"pt" => "Perdeu!",
_ => "Miss!"
},
"out" => uiLangCode switch
{
"tr" => "Ekran dışı!",
"es" => "Fuera!",
"de" => "Außerhalb!",
"fr" => "Hors écran!",
"pt" => "Fora!",
_ => "Out!"
},
_ => key
};
}
// ======================================================
Color FullAlpha(Color c)
{
c.a = 1f;
return c;
}
float EaseOutQuad(float t)
{
t = Mathf.Clamp01(t);
return 1f - (1f - t) * (1f - t);
}
float EaseOutBack(float t)
{
t = Mathf.Clamp01(t);
float c1 = 1.70158f;
float c3 = c1 + 1f;
return 1f + c3 * Mathf.Pow(t - 1f, 3f) + c1 * Mathf.Pow(t - 1f, 2f);
}
void Shuffle<T>(List<T> list)
{
for (int i = list.Count - 1; i > 0; i--)
{
int r = Random.Range(0, i + 1);
T temp = list[i];
list[i] = list[r];
list[r] = temp;
}
}
}