starOfWords/Assets/Scripts/4.0/game/Unscramble15Mod.cs

1314 lines
34 KiB
C#
Raw Normal View History

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;
}
}
}