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

1314 lines
No EOL
34 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;
using System.Globalization;
public class Unscramble15Mod : MonoBehaviour
{
[Header("━━━ TXT FILE ━━━━━━━━━━━━━━━━━━━━━━━━")]
public TextAsset wordFile;
[Header("━━━ MAIN UI ━━━━━━━━━━━━━━━━━━━━━━━━━")]
public TMP_Text questionText;
public TMP_Text languageText;
public TMP_Text feedbackText;
[Header("━━━ LETTER BUTTONS - 10 BUTTON ━━━━━━")]
public Button[] letterButtons;
public TMP_Text[] letterTexts;
[Header("━━━ 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("━━━ SCORE UI ━━━━━━━━━━━━━━━━━━━━━━━━")]
public TMP_Text scoreText;
public TMP_Text comboText;
public TMP_Text timerText;
[Header("━━━ SCENES ━━━━━━━━━━━━━━━━━━━━━━━━━━")]
public string backSceneName = "modsec 15";
public string resultSceneName = "modres 15";
[Header("━━━ LANGUAGE ━━━━━━━━━━━━━━━━━━━━━━━━")]
public string forcedLanguageCode = "";
[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("━━━ 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("━━━ QUESTION FONT ━━━━━━━━━━━━━━━━━━━")]
public bool questionUppercase = true;
public float questionFontSize = 86f;
public Color questionColor = Color.white;
public FontStyles questionFontStyle = FontStyles.Bold;
public TextAlignmentOptions questionAlignment = TextAlignmentOptions.Center;
public float questionCharacterSpacing = 8f;
[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);
[Header("━━━ ANIMATION ━━━━━━━━━━━━━━━━━━━━━━━")]
[Range(0.80f, 1f)]
public float selectedScale = 0.94f;
public float selectAnimTime = 0.10f;
public float enterAnimTime = 0.22f;
const string modKey = "Mod15";
readonly string[] langCodes = { "tr", "en", "es", "de", "fr", "pt" };
readonly string[] langNames = { "TR", "EN", "ES", "DE", "FR", "PT" };
class WordEntry
{
public string[] words = new string[6];
}
readonly List<WordEntry> wordPool = new List<WordEntry>();
int selectedLangIndex = 1;
int wordIndex = 0;
int score = 0;
int combo = 0;
int correctCount = 0;
int wrongCount = 0;
int bestComboThisRun = 0;
float timeLeft;
bool gameOver = false;
bool waitingNextQuestion = false;
bool extraTimeReady = true;
bool extraHintReady = true;
bool extraSkipReady = true;
string currentTargetWord = "";
string builtAnswer = "";
readonly List<string> scrambledLetters = new List<string>();
readonly List<int> selectedButtonIndexes = new List<int>();
bool[] usedButtons;
Vector3[] originalButtonScales;
Vector3[] originalButtonPositions;
Coroutine[] buttonScaleCoroutines;
Coroutine[] buttonEnterCoroutines;
readonly Color timerNormalColor = Color.white;
readonly Color timerWarnColor = new Color32(255, 80, 80, 255);
void Awake()
{
LoadWords();
}
void Start()
{
selectedLangIndex = ResolveLanguageIndex();
timeLeft = PlayerPrefs.GetFloat("SelectedGameTime", startTime);
if (timeLeft <= 0)
timeLeft = startTime;
if (wordPool.Count < 1)
{
Debug.LogError("Mod15 TXT boş veya hatalı!");
enabled = false;
return;
}
if (letterButtons == null || letterButtons.Length < 10 || letterTexts == null || letterTexts.Length < 10)
{
Debug.LogError("Mod15 için 10 LetterButton ve 10 LetterText atanmalı!");
enabled = false;
return;
}
HideCooldownTexts();
CacheButtonData();
ForceButtonTransitionNone();
PrepareQuestionText();
PrepareFeedbackText();
BindMainButtons();
Shuffle(wordPool);
UpdateScore();
UpdateCombo();
UpdateTimerUI();
UpdateLanguageUI();
NextQuestion();
}
void Update()
{
if (gameOver)
return;
timeLeft -= Time.deltaTime;
if (timeLeft <= 0)
{
timeLeft = 0;
UpdateTimerUI();
GameOver();
return;
}
UpdateTimerUI();
}
void LoadWords()
{
wordPool.Clear();
if (wordFile == null)
{
Debug.LogError("Unscramble15 TXT atanmadı!");
return;
}
string[] lines = wordFile.text.Split(
new[] { '\n', '\r' },
System.StringSplitOptions.RemoveEmptyEntries
);
for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
{
string line = lines[lineIndex].Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
if (line.ToLower().StartsWith("tr,en,es,de,fr,pt"))
continue;
string[] p = SplitLineSmart(line);
if (p.Length < 6)
{
Debug.LogWarning("Mod15 eksik satır atlandı. Satır: " + (lineIndex + 1));
continue;
}
WordEntry e = new WordEntry();
for (int i = 0; i < 6; i++)
e.words[i] = CleanField(p[i]);
wordPool.Add(e);
}
}
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();
}
int ResolveLanguageIndex()
{
string code = forcedLanguageCode.Trim().ToLower();
if (string.IsNullOrEmpty(code))
code = PlayerPrefs.GetString("UnscrambleLangCode", "").Trim().ToLower();
if (string.IsNullOrEmpty(code))
code = PlayerPrefs.GetString("QuestionLangCode", "").Trim().ToLower();
if (!string.IsNullOrEmpty(code))
{
for (int i = 0; i < langCodes.Length; i++)
{
if (code == langCodes[i])
return i;
}
}
int appLang = PlayerPrefs.GetInt("AppLanguage", 1);
if (appLang < 0 || appLang > 5)
appLang = 1;
return appLang;
}
void PrepareQuestionText()
{
if (questionText == null)
return;
questionText.fontSize = questionFontSize;
questionText.color = FullAlpha(questionColor);
questionText.fontStyle = questionFontStyle;
questionText.alignment = questionAlignment;
questionText.characterSpacing = questionCharacterSpacing;
// Unity 6 uyumlu yeni kullanım
questionText.textWrappingMode = TextWrappingModes.Normal;
questionText.overflowMode = TextOverflowModes.Overflow;
}
void PrepareFeedbackText()
{
if (feedbackText == null)
return;
feedbackText.alignment = TextAlignmentOptions.Center;
feedbackText.fontSize = Mathf.Max(feedbackText.fontSize, 60f);
feedbackText.text = "";
feedbackText.color = FullAlpha(feedbackText.color);
}
void UpdateLanguageUI()
{
if (languageText != null)
languageText.text = "LANG: " + langNames[selectedLangIndex];
}
void BindMainButtons()
{
if (submitButton != null)
{
submitButton.onClick.RemoveAllListeners();
submitButton.onClick.AddListener(OnSubmit);
}
if (extraTimeButton != null)
{
extraTimeButton.onClick.RemoveAllListeners();
extraTimeButton.onClick.AddListener(OnExtraTime);
}
if (extraHintButton != null)
{
extraHintButton.onClick.RemoveAllListeners();
extraHintButton.onClick.AddListener(OnExtraHint5050);
}
if (extraSkipButton != null)
{
extraSkipButton.onClick.RemoveAllListeners();
extraSkipButton.onClick.AddListener(OnExtraSkip);
}
if (backButton != null)
{
backButton.onClick.RemoveAllListeners();
backButton.onClick.AddListener(() =>
{
SceneManager.LoadScene(backSceneName);
});
}
}
void BindLetterButtons()
{
for (int i = 0; i < letterButtons.Length; i++)
{
int index = i;
if (letterButtons[i] == null)
continue;
letterButtons[i].onClick.RemoveAllListeners();
letterButtons[i].onClick.AddListener(() =>
{
ToggleLetter(index);
});
}
}
void HideCooldownTexts()
{
if (extraTimeCooldownText != null)
extraTimeCooldownText.gameObject.SetActive(false);
if (extraHintCooldownText != null)
extraHintCooldownText.gameObject.SetActive(false);
if (extraSkipCooldownText != null)
extraSkipCooldownText.gameObject.SetActive(false);
}
void CacheButtonData()
{
int len = letterButtons.Length;
originalButtonScales = new Vector3[len];
originalButtonPositions = new Vector3[len];
buttonScaleCoroutines = new Coroutine[len];
buttonEnterCoroutines = new Coroutine[len];
for (int i = 0; i < len; i++)
{
if (letterButtons[i] == null)
continue;
originalButtonScales[i] = letterButtons[i].transform.localScale;
originalButtonPositions[i] = letterButtons[i].transform.localPosition;
}
}
void ForceButtonTransitionNone()
{
for (int i = 0; i < letterButtons.Length; i++)
{
if (letterButtons[i] == null)
continue;
letterButtons[i].transition = Selectable.Transition.None;
Image img = letterButtons[i].image;
if (img != null)
img.color = FullAlpha(normalButtonColor);
}
}
void NextQuestion()
{
if (gameOver)
return;
waitingNextQuestion = false;
builtAnswer = "";
selectedButtonIndexes.Clear();
scrambledLetters.Clear();
ResetAllLetterButtons();
string target = GetNextValidTargetWord();
if (string.IsNullOrEmpty(target))
{
Debug.LogError("Mod15 için 10 harften kısa/uygun kelime bulunamadı!");
GameOver();
return;
}
currentTargetWord = target;
List<string> letters = WordToLetterList(currentTargetWord);
scrambledLetters.AddRange(ScrambleLetters(letters));
usedButtons = new bool[letterButtons.Length];
for (int i = 0; i < letterButtons.Length; i++)
{
if (letterButtons[i] == null)
continue;
if (i < scrambledLetters.Count)
{
letterButtons[i].gameObject.SetActive(true);
letterButtons[i].interactable = true;
Image img = letterButtons[i].image;
if (img != null)
img.color = FullAlpha(normalButtonColor);
if (letterTexts[i] != null)
letterTexts[i].text = scrambledLetters[i];
usedButtons[i] = false;
}
else
{
letterButtons[i].gameObject.SetActive(false);
}
}
BindLetterButtons();
UpdateQuestionSlots();
if (feedbackText != null)
feedbackText.text = "";
if (submitButton != null)
submitButton.interactable = true;
for (int i = 0; i < scrambledLetters.Count; i++)
{
if (letterButtons[i] == null)
continue;
if (buttonEnterCoroutines[i] != null)
StopCoroutine(buttonEnterCoroutines[i]);
buttonEnterCoroutines[i] = StartCoroutine(ButtonEnterAnim(letterButtons[i], i));
}
}
string GetNextValidTargetWord()
{
int maxLength = Mathf.Min(10, letterButtons.Length);
int safety = wordPool.Count;
for (int attempt = 0; attempt < safety; attempt++)
{
if (wordIndex >= wordPool.Count)
{
wordIndex = 0;
Shuffle(wordPool);
}
WordEntry chosen = wordPool[wordIndex++];
string target = PrepareTargetWord(chosen.words[selectedLangIndex]);
if (!string.IsNullOrEmpty(target) && target.Length >= 2 && target.Length <= maxLength)
return target;
}
return "";
}
string PrepareTargetWord(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
return "";
string result = "";
foreach (char c in raw)
{
if (char.IsLetter(c))
{
string ch = c.ToString();
if (questionUppercase)
ch = UpperByLanguage(ch);
result += ch;
}
}
return result;
}
List<string> WordToLetterList(string word)
{
List<string> letters = new List<string>();
for (int i = 0; i < word.Length; i++)
letters.Add(word[i].ToString());
return letters;
}
List<string> ScrambleLetters(List<string> originalLetters)
{
List<string> mixed = new List<string>(originalLetters);
if (mixed.Count <= 2)
{
Shuffle(mixed);
return mixed;
}
string original = string.Join("", originalLetters);
for (int attempt = 0; attempt < 30; attempt++)
{
Shuffle(mixed);
string test = string.Join("", mixed);
if (test != original)
return mixed;
}
return mixed;
}
void ToggleLetter(int index)
{
if (gameOver || waitingNextQuestion)
return;
if (index < 0 || index >= letterButtons.Length)
return;
if (index >= scrambledLetters.Count)
return;
if (usedButtons == null)
return;
if (usedButtons[index])
CancelLetter(index);
else
SelectLetter(index);
}
void SelectLetter(int index)
{
usedButtons[index] = true;
if (!selectedButtonIndexes.Contains(index))
selectedButtonIndexes.Add(index);
RebuildAnswerFromSelectedButtons();
if (letterButtons[index] != null)
{
Image img = letterButtons[index].image;
if (img != null)
img.color = FullAlpha(selectedButtonColor);
StartScaleAnim(index, originalButtonScales[index] * selectedScale);
}
UpdateQuestionSlots();
}
void CancelLetter(int index)
{
usedButtons[index] = false;
if (selectedButtonIndexes.Contains(index))
selectedButtonIndexes.Remove(index);
RebuildAnswerFromSelectedButtons();
if (letterButtons[index] != null)
{
Image img = letterButtons[index].image;
if (img != null)
img.color = FullAlpha(normalButtonColor);
StartScaleAnim(index, originalButtonScales[index]);
}
UpdateQuestionSlots();
}
void RebuildAnswerFromSelectedButtons()
{
builtAnswer = "";
for (int i = 0; i < selectedButtonIndexes.Count; i++)
{
int btnIndex = selectedButtonIndexes[i];
if (btnIndex >= 0 && btnIndex < scrambledLetters.Count)
builtAnswer += scrambledLetters[btnIndex];
}
}
void UpdateQuestionSlots()
{
List<string> display = new List<string>();
for (int i = 0; i < currentTargetWord.Length; i++)
{
if (i < builtAnswer.Length)
display.Add(builtAnswer[i].ToString());
else
display.Add("_");
}
if (questionText != null)
questionText.text = string.Join(" ", display);
}
void OnSubmit()
{
if (gameOver || waitingNextQuestion)
return;
if (builtAnswer.Length < currentTargetWord.Length)
{
if (feedbackText != null)
{
feedbackText.color = FullAlpha(wrongButtonColor);
feedbackText.text = "COMPLETE THE WORD";
StartCoroutine(FeedbackAnim(feedbackText));
}
return;
}
waitingNextQuestion = true;
if (submitButton != null)
submitButton.interactable = false;
LockLetterButtons();
if (builtAnswer == currentTargetWord)
HandleCorrectAnswer();
else
HandleWrongAnswer();
UpdateScore();
UpdateCombo();
StartCoroutine(DelayNext());
}
void LockLetterButtons()
{
for (int i = 0; i < scrambledLetters.Count; i++)
{
if (letterButtons[i] == null)
continue;
letterButtons[i].interactable = false;
}
}
void HandleCorrectAnswer()
{
for (int i = 0; i < selectedButtonIndexes.Count; i++)
{
int index = selectedButtonIndexes[i];
if (index < 0 || index >= letterButtons.Length)
continue;
if (letterButtons[index] == null)
continue;
Image img = letterButtons[index].image;
if (img != null)
img.color = FullAlpha(correctButtonColor);
StartCoroutine(CorrectPulse(letterButtons[index], index));
}
combo++;
correctCount++;
if (combo > bestComboThisRun)
bestComboThisRun = combo;
score += Mathf.RoundToInt(basePoints * GetComboMultiplier(combo));
timeLeft += correctTimeBonus;
if (feedbackText != null)
{
feedbackText.color = FullAlpha(correctButtonColor);
feedbackText.text = combo >= 3 ? "CORRECT " + combo + "x COMBO!" : "CORRECT";
StartCoroutine(FeedbackAnim(feedbackText));
}
if (comboText != null)
StartCoroutine(ComboAnim(comboText));
if (scoreText != null)
StartCoroutine(ScorePopAnim(scoreText));
}
void HandleWrongAnswer()
{
for (int i = 0; i < selectedButtonIndexes.Count; i++)
{
int index = selectedButtonIndexes[i];
if (index < 0 || index >= letterButtons.Length)
continue;
if (letterButtons[index] == null)
continue;
Image img = letterButtons[index].image;
if (img != null)
img.color = FullAlpha(wrongButtonColor);
StartCoroutine(ShakeButton(letterButtons[index], index));
}
wrongCount++;
combo = 0;
score -= wrongPenalty;
if (score < 0)
score = 0;
if (questionText != null)
questionText.text = FormatWithSpaces(currentTargetWord);
if (feedbackText != null)
{
feedbackText.color = FullAlpha(wrongButtonColor);
feedbackText.text = "WRONG\nCorrect: " + FormatWithSpaces(currentTargetWord);
StartCoroutine(FeedbackAnim(feedbackText));
}
}
IEnumerator DelayNext()
{
yield return new WaitForSeconds(nextQuestionDelay);
if (!gameOver)
NextQuestion();
}
void OnExtraTime()
{
if (!extraTimeReady || gameOver)
return;
timeLeft += 5f;
extraTimeReady = false;
if (extraTimeButton != null)
extraTimeButton.interactable = false;
StartCoroutine(Cooldown(extraTimeCooldownText, extraTimeButton, () => extraTimeReady = true));
}
void OnExtraHint5050()
{
if (!extraHintReady || gameOver || waitingNextQuestion)
return;
int autoSelected = 0;
while (autoSelected < 2 && builtAnswer.Length < currentTargetWord.Length)
{
int nextPosition = builtAnswer.Length;
string neededLetter = currentTargetWord[nextPosition].ToString();
int foundIndex = FindUnusedButtonWithLetter(neededLetter);
if (foundIndex == -1)
break;
SelectLetter(foundIndex);
autoSelected++;
}
extraHintReady = false;
if (extraHintButton != null)
extraHintButton.interactable = false;
StartCoroutine(Cooldown(extraHintCooldownText, extraHintButton, () => extraHintReady = true));
}
int FindUnusedButtonWithLetter(string neededLetter)
{
for (int i = 0; i < scrambledLetters.Count; i++)
{
if (usedButtons[i])
continue;
if (scrambledLetters[i] == neededLetter)
return i;
}
return -1;
}
void OnExtraSkip()
{
if (!extraSkipReady || gameOver || waitingNextQuestion)
return;
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 = 5f;
if (label != null)
label.gameObject.SetActive(true);
while (t > 0f)
{
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 GameOver()
{
if (gameOver)
return;
gameOver = true;
PlayerPrefs.SetString("LastPlayedMod", modKey);
PlayerPrefs.SetInt("Mod15Skor", score);
PlayerPrefs.SetInt("Mod15Score", score);
PlayerPrefs.SetInt("Mod15Correct", correctCount);
PlayerPrefs.SetInt("Mod15Wrong", 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 totalAnswers = correctCount + wrongCount;
int accuracy = totalAnswers > 0 ? Mathf.RoundToInt((correctCount / (float)totalAnswers) * 100f) : 0;
PlayerPrefs.SetInt(modKey + "_LastAccuracy", accuracy);
if (score > PlayerPrefs.GetInt(modKey + "_MaxScore", 0))
PlayerPrefs.SetInt(modKey + "_MaxScore", score);
if (bestComboThisRun > PlayerPrefs.GetInt(modKey + "_BestCombo", 0))
PlayerPrefs.SetInt(modKey + "_BestCombo", bestComboThisRun);
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 UpdateScore()
{
if (scoreText != null)
scoreText.text = score.ToString();
}
void UpdateCombo()
{
if (comboText != null)
comboText.text = combo + "x";
}
void UpdateTimerUI()
{
if (timerText == null)
return;
timerText.text = Mathf.CeilToInt(timeLeft).ToString();
if (timeLeft <= 10f)
{
timerText.color = FullAlpha(Color.Lerp(timerWarnColor, 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;
}
}
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;
}
string FormatWithSpaces(string word)
{
List<string> letters = new List<string>();
for (int i = 0; i < word.Length; i++)
letters.Add(word[i].ToString());
return string.Join(" ", letters);
}
string UpperByLanguage(string text)
{
if (string.IsNullOrEmpty(text))
return "";
CultureInfo culture = CultureInfo.InvariantCulture;
switch (langCodes[selectedLangIndex])
{
case "tr":
culture = new CultureInfo("tr-TR");
break;
case "es":
culture = new CultureInfo("es-ES");
break;
case "de":
culture = new CultureInfo("de-DE");
break;
case "fr":
culture = new CultureInfo("fr-FR");
break;
case "pt":
culture = new CultureInfo("pt-BR");
break;
default:
culture = CultureInfo.InvariantCulture;
break;
}
return text.ToUpper(culture);
}
void ResetAllLetterButtons()
{
for (int i = 0; i < letterButtons.Length; i++)
{
if (letterButtons[i] == null)
continue;
StopButtonCoroutines(i);
letterButtons[i].gameObject.SetActive(true);
letterButtons[i].interactable = true;
letterButtons[i].transition = Selectable.Transition.None;
Image img = letterButtons[i].image;
if (img != null)
img.color = FullAlpha(normalButtonColor);
letterButtons[i].transform.localScale = originalButtonScales[i];
letterButtons[i].transform.localPosition = originalButtonPositions[i];
if (letterTexts != null && i < letterTexts.Length && letterTexts[i] != null)
letterTexts[i].text = "";
}
}
void StartScaleAnim(int index, Vector3 targetScale)
{
if (index < 0 || index >= letterButtons.Length)
return;
if (letterButtons[index] == null)
return;
if (buttonScaleCoroutines[index] != null)
StopCoroutine(buttonScaleCoroutines[index]);
buttonScaleCoroutines[index] = StartCoroutine(ScaleButton(letterButtons[index], targetScale));
}
void StopButtonCoroutines(int index)
{
if (buttonScaleCoroutines != null && index < buttonScaleCoroutines.Length && buttonScaleCoroutines[index] != null)
{
StopCoroutine(buttonScaleCoroutines[index]);
buttonScaleCoroutines[index] = null;
}
if (buttonEnterCoroutines != null && index < buttonEnterCoroutines.Length && buttonEnterCoroutines[index] != null)
{
StopCoroutine(buttonEnterCoroutines[index]);
buttonEnterCoroutines[index] = null;
}
}
IEnumerator ScaleButton(Button btn, Vector3 targetScale)
{
Vector3 startScale = btn.transform.localScale;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / Mathf.Max(0.01f, selectAnimTime);
btn.transform.localScale = Vector3.Lerp(startScale, targetScale, EaseOutQuad(t));
yield return null;
}
btn.transform.localScale = targetScale;
}
IEnumerator ButtonEnterAnim(Button btn, int delayIndex)
{
int idx = System.Array.IndexOf(letterButtons, btn);
if (idx < 0)
yield break;
yield return new WaitForSeconds(delayIndex * 0.04f);
Vector3 targetPos = originalButtonPositions[idx];
Vector3 startPos = targetPos + Vector3.up * 25f;
Vector3 targetScale = originalButtonScales[idx];
Vector3 startScale = targetScale * 0.85f;
btn.transform.localPosition = startPos;
btn.transform.localScale = startScale;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / Mathf.Max(0.01f, enterAnimTime);
float ease = EaseOutBack(t);
btn.transform.localPosition = Vector3.Lerp(startPos, targetPos, ease);
btn.transform.localScale = Vector3.Lerp(startScale, targetScale, ease);
yield return null;
}
btn.transform.localPosition = targetPos;
btn.transform.localScale = targetScale;
}
IEnumerator CorrectPulse(Button btn, int idx)
{
Vector3 normal = originalButtonScales[idx];
Vector3 big = normal * 1.12f;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
btn.transform.localScale = Vector3.Lerp(normal, big, EaseOutQuad(t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
btn.transform.localScale = Vector3.Lerp(big, normal, EaseOutQuad(t));
yield return null;
}
btn.transform.localScale = normal;
}
IEnumerator ShakeButton(Button btn, int idx)
{
Vector3 origin = originalButtonPositions[idx];
float duration = 0.35f;
float magnitude = 12f;
float elapsed = 0f;
while (elapsed < duration)
{
float power = 1f - elapsed / duration;
float x = Random.Range(-1f, 1f) * magnitude * power;
btn.transform.localPosition = origin + new Vector3(x, 0f, 0f);
elapsed += Time.deltaTime;
yield return null;
}
btn.transform.localPosition = origin;
}
IEnumerator FeedbackAnim(TMP_Text txt)
{
Vector3 originalScale = txt.transform.localScale;
Vector3 bigScale = originalScale * 1.18f;
Color c = FullAlpha(txt.color);
c.a = 0f;
txt.color = c;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.16f;
txt.transform.localScale = Vector3.Lerp(originalScale, bigScale, EaseOutBack(t));
c.a = Mathf.Clamp01(t);
txt.color = c;
yield return null;
}
c.a = 1f;
txt.color = c;
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
txt.transform.localScale = Vector3.Lerp(bigScale, originalScale, EaseOutQuad(t));
yield return null;
}
txt.transform.localScale = originalScale;
}
IEnumerator ComboAnim(TMP_Text txt)
{
Vector3 original = txt.transform.localScale;
Vector3 big = original * 1.22f;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
txt.transform.localScale = Vector3.Lerp(original, big, EaseOutBack(t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.14f;
txt.transform.localScale = Vector3.Lerp(big, original, EaseOutQuad(t));
yield return null;
}
txt.transform.localScale = original;
}
IEnumerator ScorePopAnim(TMP_Text txt)
{
Vector3 original = txt.transform.localScale;
Vector3 big = original * 1.18f;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.10f;
txt.transform.localScale = Vector3.Lerp(original, big, EaseOutQuad(t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
txt.transform.localScale = Vector3.Lerp(big, original, EaseOutQuad(t));
yield return null;
}
txt.transform.localScale = original;
}
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);
}
float EaseOutQuad(float t)
{
t = Mathf.Clamp01(t);
return 1f - (1f - t) * (1f - t);
}
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;
}
}
}