starOfWords/Assets/Scripts/3.0/mod10/SentenceOrderGameScene.cs

807 lines
22 KiB
C#
Raw Normal View History

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SentenceOrderGameScene : MonoBehaviour
{
[Header("TXT DOSYASI (6 Dil)")]
public TextAsset questionFile;
[Header("UI")]
public TMP_Text questionText;
public TMP_Text[] answerTexts;
public Button[] answerButtons;
public Button submitButton;
public TMP_Text selectedWordsText;
public TMP_Text scoreText;
public TMP_Text comboText;
public TMP_Text timerText;
public TMP_Text feedbackText;
[Header("EXTRA BUTTONS")]
public Button extraTimeButton;
public Button extraHintButton;
public Button extraSkipButton;
[Header("━━━ BACK BUTTON ━━━━━━━━━━━━━━━━━━━━━━━━")]
public Button backButton;
public string backSceneName = "ModSec 10";
[Header("RENKLER")]
public Color normalColor = new Color32(255, 255, 255, 70);
public Color selectedColor = new Color32(60, 110, 200, 255);
public Color correctColor = new Color32(0, 200, 0, 255);
public Color wrongColor = new Color32(200, 0, 0, 255);
[Header("━━━ FEEDBACK TEXT SETTINGS ━━━━━━━━━━━━━")]
public bool feedbackOnTop = true;
public float feedbackTopY = 400f;
public float feedbackFontSize = 72f;
[Header("━━━ ANIMATION SETTINGS ━━━━━━━━━━━━━━━━━")]
[Range(0.80f, 1f)] public float selectedScale = 0.94f;
public float selectAnimTime = 0.10f;
public float enterAnimTime = 0.22f;
// INTERNAL
private class SentenceEntry
{
public string[] sentence = new string[6];
}
private List<SentenceEntry> questions = new List<SentenceEntry>();
private string[] correctWords;
private string[] scrambledWords;
private int[] order;
private int selectedCount = 0;
private bool locked = false;
private int lastID = -1;
private int score = 0;
private int combo = 0;
private float timeLeft = 60f;
private float nextTimer = 0f;
// LANGUAGE & MOD KEY
private string lang;
private string modKey = "Mod10";
// Counters
private int correctCount = 0;
private int wrongCount = 0;
readonly Color timerNormalColor = Color.white;
readonly Color timerWarnColor = new Color32(255, 80, 80, 255);
Vector3[] originalButtonScales;
Vector3[] originalButtonPositions;
Coroutine[] buttonScaleCoroutines;
Coroutine[] buttonEnterCoroutines;
Coroutine feedbackCo;
// ------------------------------------------------------------
void Start()
{
lang = PlayerPrefs.GetString("QuestionLangCode", "en");
order = new int[answerButtons.Length];
if (questionFile == null)
{
Debug.LogError("SentenceOrderGameScene: TXT bağlanmamış!");
return;
}
CacheButtonData();
PrepareFeedbackText();
LoadTXT(questionFile.text);
Shuffle(questions);
submitButton.onClick.AddListener(OnSubmit);
extraTimeButton.onClick.AddListener(() => timeLeft += 5f);
extraHintButton.onClick.AddListener(OnHint);
extraSkipButton.onClick.AddListener(() => NextQuestion());
if (backButton != null)
{
backButton.onClick.RemoveAllListeners();
backButton.onClick.AddListener(() => SceneManager.LoadScene(backSceneName));
}
scoreText.text = "0";
comboText.text = "0x";
NextQuestion();
}
// ------------------------------------------------------------
void Update()
{
if (!locked)
{
timeLeft -= Time.deltaTime;
if (timeLeft <= 0)
{
timeLeft = 0;
GameOver();
}
}
UpdateTimerUI();
if (locked && nextTimer > 0)
{
nextTimer -= Time.deltaTime;
if (nextTimer <= 0)
NextQuestion();
}
}
// ------------------------------------------------------------
void CacheButtonData()
{
if (answerButtons == null) return;
int len = answerButtons.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 (answerButtons[i] == null) continue;
originalButtonScales[i] = answerButtons[i].transform.localScale;
originalButtonPositions[i] = answerButtons[i].transform.localPosition;
answerButtons[i].transition = Selectable.Transition.None;
}
}
void PrepareFeedbackText()
{
if (feedbackText == null) return;
if (feedbackOnTop)
{
Vector3 pos = feedbackText.transform.localPosition;
pos.y = feedbackTopY;
feedbackText.transform.localPosition = pos;
}
feedbackText.alignment = TextAlignmentOptions.Center;
feedbackText.fontSize = Mathf.Max(feedbackText.fontSize, feedbackFontSize);
feedbackText.text = "";
Color c = feedbackText.color;
c.a = 1f;
feedbackText.color = c;
}
void UpdateTimerUI()
{
if (timerText == null) return;
timerText.text = Mathf.Ceil(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;
}
}
// ------------------------------------------------------------
void LoadTXT(string raw)
{
foreach (string line in raw.Trim().Split('\n'))
{
if (string.IsNullOrWhiteSpace(line)) continue;
string[] p = line.Split(',');
if (p.Length < 6) continue;
SentenceEntry e = new SentenceEntry();
for (int i = 0; i < 6; i++)
e.sentence[i] = p[i].Trim();
questions.Add(e);
}
}
// ------------------------------------------------------------
void NextQuestion()
{
nextTimer = 0f;
locked = false;
selectedCount = 0;
if (feedbackText != null)
feedbackText.text = "";
if (selectedWordsText != null)
selectedWordsText.text = "";
for (int i = 0; i < order.Length; i++)
order[i] = 0;
for (int i = 0; i < answerButtons.Length; i++)
{
StopButtonCoroutines(i);
answerButtons[i].interactable = true;
answerButtons[i].image.color = FullAlpha(normalColor);
answerButtons[i].transform.localScale = originalButtonScales[i];
answerButtons[i].transform.localPosition = originalButtonPositions[i];
answerButtons[i].gameObject.SetActive(false);
}
// random
int qID = Random.Range(0, questions.Count);
if (qID == lastID && questions.Count > 1)
qID = (qID + 1) % questions.Count;
lastID = qID;
SentenceEntry entry = questions[qID];
int L = GetLangIndex();
string s = entry.sentence[L];
questionText.text = s;
StartCoroutine(FadeInText(questionText));
correctWords = s.Split(' ');
int wc = Mathf.Min(correctWords.Length, answerButtons.Length);
System.Array.Resize(ref correctWords, wc);
scrambledWords = (string[])correctWords.Clone();
Shuffle(scrambledWords);
for (int i = 0; i < wc; i++)
{
answerButtons[i].gameObject.SetActive(true);
answerTexts[i].text = scrambledWords[i];
int id = i;
answerButtons[i].onClick.RemoveAllListeners();
answerButtons[i].onClick.AddListener(() => OnClickWord(id));
buttonEnterCoroutines[i] = StartCoroutine(ButtonEnterAnim(answerButtons[i], i));
}
}
// ------------------------------------------------------------
void OnClickWord(int id)
{
if (locked) return;
int wc = correctWords.Length;
if (order[id] != 0)
{
int old = order[id];
order[id] = 0;
for (int i = 0; i < wc; i++)
if (order[i] > old)
order[i]--;
selectedCount--;
}
else
{
selectedCount++;
order[id] = selectedCount;
}
RefreshUI();
}
// ------------------------------------------------------------
void RefreshUI()
{
int wc = correctWords.Length;
List<string> seq = new List<string>();
for (int i = 0; i < wc; i++)
{
if (order[i] > 0)
{
answerButtons[i].image.color = FullAlpha(selectedColor);
answerTexts[i].text = order[i] + ". " + scrambledWords[i];
StartScaleAnim(i, originalButtonScales[i] * selectedScale);
}
else
{
answerButtons[i].image.color = FullAlpha(normalColor);
answerTexts[i].text = scrambledWords[i];
StartScaleAnim(i, originalButtonScales[i]);
}
}
for (int orderNum = 1; orderNum <= wc; orderNum++)
for (int j = 0; j < wc; j++)
if (order[j] == orderNum)
seq.Add(scrambledWords[j]);
if (selectedWordsText != null)
selectedWordsText.text = string.Join(" | ", seq);
}
// ------------------------------------------------------------
void OnSubmit()
{
if (locked) return;
int wc = correctWords.Length;
if (selectedCount < wc)
{
SetFeedback("Select all words!", wrongColor);
return;
}
locked = true;
bool ok = true;
for (int orderNum = 1; orderNum <= wc; orderNum++)
for (int i = 0; i < wc; i++)
if (order[i] == orderNum && scrambledWords[i] != correctWords[orderNum - 1])
ok = false;
for (int i = 0; i < wc; i++)
answerButtons[i].interactable = false;
if (ok)
{
correctCount++;
combo++;
score += 50 + (combo - 1) * 10;
timeLeft += 2f;
for (int i = 0; i < wc; i++)
{
answerButtons[i].image.color = FullAlpha(correctColor);
StartCoroutine(CorrectPulse(answerButtons[i], i));
}
SetFeedback(combo >= 3 ? $"TRUE {combo}x COMBO!" : "TRUE", correctColor);
if (comboText != null) StartCoroutine(ComboAnim(comboText));
}
else
{
wrongCount++;
combo = 0;
for (int i = 0; i < wc; i++)
{
answerButtons[i].image.color = FullAlpha(wrongColor);
StartCoroutine(ShakeButton(answerButtons[i], i));
}
SetFeedback("FALSE", wrongColor);
}
scoreText.text = score.ToString();
comboText.text = combo + "x";
if (scoreText != null) StartCoroutine(ScorePopAnim(scoreText));
nextTimer = 1f;
}
// ------------------------------------------------------------
// HINT: İlk kelimeyi otomatik seçer
// ------------------------------------------------------------
void OnHint()
{
if (locked) return;
int wc = correctWords.Length;
// Önce tüm seçimleri sıfırla
selectedCount = 0;
for (int i = 0; i < wc; i++)
order[i] = 0;
// Doğru ilk kelime
string firstWord = correctWords[0];
// Scrambled içinde doğru kelimenin index'ini bul
for (int i = 0; i < wc; i++)
{
if (scrambledWords[i] == firstWord)
{
order[i] = 1;
selectedCount = 1;
StartCoroutine(CorrectPulse(answerButtons[i], i));
break;
}
}
RefreshUI();
SetFeedback("Hint used!", selectedColor);
}
// ------------------------------------------------------------
void SetFeedback(string msg, Color c)
{
if (feedbackText == null) return;
feedbackText.text = msg;
feedbackText.color = FullAlpha(c);
if (feedbackOnTop)
{
Vector3 pos = feedbackText.transform.localPosition;
pos.y = feedbackTopY;
feedbackText.transform.localPosition = pos;
}
if (feedbackCo != null) StopCoroutine(feedbackCo);
feedbackCo = StartCoroutine(FeedbackAnim(feedbackText));
}
// ------------------------------------------------------------
void GameOver()
{
// 🔥 Mod Select / Leaderboard ekranına direkt bağlanacak basit key
PlayerPrefs.SetInt("Mod10Skor", score);
// kayıt sistemi
PlayerPrefs.SetString("LastPlayedMod", modKey);
PlayerPrefs.SetInt(modKey + "_LastScore", score);
int oldMax = PlayerPrefs.GetInt(modKey + "_MaxScore", 0);
if (score > oldMax)
PlayerPrefs.SetInt(modKey + "_MaxScore", score);
PlayerPrefs.SetInt(modKey + "_Correct", correctCount);
PlayerPrefs.SetInt(modKey + "_Wrong", wrongCount);
PlayerPrefs.Save();
// 🔥 MOD 10 SONUÇ SAHNESİ
SceneManager.LoadScene("Mod10Resume");
}
// ------------------------------------------------------------
int GetLangIndex()
{
return lang switch
{
"tr" => 0,
"en" => 1,
"es" => 2,
"de" => 3,
"fr" => 4,
"pt" => 5,
_ => 1
};
}
// ------------------------------------------------------------
void Shuffle<T>(IList<T> arr)
{
for (int i = 0; i < arr.Count; i++)
{
int r = Random.Range(i, arr.Count);
(arr[i], arr[r]) = (arr[r], arr[i]);
}
}
// ================== ANIMATION HELPERS ==================
void StartScaleAnim(int index, Vector3 targetScale)
{
if (index < 0 || index >= answerButtons.Length) return;
if (answerButtons[index] == null) return;
if (buttonScaleCoroutines[index] != null)
StopCoroutine(buttonScaleCoroutines[index]);
buttonScaleCoroutines[index] = StartCoroutine(ScaleButton(answerButtons[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)
{
if (btn == null) yield break;
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)
{
if (btn == null) yield break;
int idx = System.Array.IndexOf(answerButtons, btn);
if (idx < 0) yield break;
yield return new WaitForSeconds(delayIndex * 0.05f);
Vector3 targetPos = originalButtonPositions[idx];
Vector3 startPos = targetPos + Vector3.up * 30f;
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)
{
if (btn == null) yield break;
if (idx < 0 || idx >= originalButtonScales.Length) yield break;
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)
{
if (btn == null) yield break;
if (idx < 0 || idx >= originalButtonPositions.Length) yield break;
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)
{
if (txt == null) yield break;
Vector3 originalScale = Vector3.one;
Vector3 bigScale = originalScale * 1.18f;
Color c = FullAlpha(txt.color);
c.a = 0f;
txt.color = c;
Vector3 originalPos = txt.transform.localPosition;
Vector3 startPos = originalPos + Vector3.down * 18f;
txt.transform.localPosition = startPos;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.16f;
float ease = EaseOutBack(t);
txt.transform.localScale = Vector3.Lerp(originalScale, bigScale, ease);
txt.transform.localPosition = Vector3.Lerp(startPos, originalPos, ease);
c.a = Mathf.Clamp01(t);
txt.color = c;
yield return null;
}
c.a = 1f;
txt.color = c;
txt.transform.localPosition = originalPos;
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;
feedbackCo = null;
}
IEnumerator ComboAnim(TMP_Text txt)
{
if (txt == null) yield break;
Vector3 original = Vector3.one;
Vector3 big = original * 1.22f;
Color baseColor = FullAlpha(txt.color);
Color highlight = new Color32(255, 220, 50, 255);
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.12f;
txt.transform.localScale = Vector3.Lerp(original, big, EaseOutBack(t));
txt.color = FullAlpha(Color.Lerp(baseColor, highlight, t));
yield return null;
}
t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.14f;
txt.transform.localScale = Vector3.Lerp(big, original, EaseOutQuad(t));
txt.color = FullAlpha(Color.Lerp(highlight, baseColor, t));
yield return null;
}
txt.transform.localScale = original;
txt.color = baseColor;
}
IEnumerator ScorePopAnim(TMP_Text txt)
{
if (txt == null) yield break;
Vector3 original = Vector3.one;
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;
}
IEnumerator FadeInText(TMP_Text txt)
{
if (txt == null) yield break;
Color c = FullAlpha(txt.color);
c.a = 0f;
txt.color = c;
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime / 0.18f;
c.a = EaseOutQuad(t);
txt.color = c;
yield return null;
}
c.a = 1f;
txt.color = c;
}
Color FullAlpha(Color c)
{
c.a = 1f;
return c;
}
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);
}
}