starOfWords/Assets/Scripts/3.0/mod7/TetrisTranslationDropMod.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

1118 lines
No EOL
30 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 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;
}
}
}