Commit e97f5ca0 authored by Yousef Sameh's avatar Yousef Sameh

Merge remote-tracking branch 'origin/pt_refactor_managers' into pt_supabase_authentication

# Conflicts:
#	My project/Assets/Scenes/MCQ/OLD MSQ.unity
#	My project/Assets/ScienceStreet/CS/Scripts/CsGameManager.cs
#	My project/UserSettings/EditorUserSettings.asset
#	My project/UserSettings/Layouts/CurrentMaximizeLayout.dwlt
parents 1873ab73 7e083792
...@@ -949,6 +949,10 @@ PrefabInstance: ...@@ -949,6 +949,10 @@ PrefabInstance:
propertyPath: m_IsActive propertyPath: m_IsActive
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 2524966151836437270, guid: adacfd07019fd8f4bbd5a9347da9f201, type: 3}
propertyPath: 'gameSceneNames.Array.data[2]'
value: oldmcq
objectReference: {fileID: 0}
- target: {fileID: 3214242843135042761, guid: adacfd07019fd8f4bbd5a9347da9f201, type: 3} - target: {fileID: 3214242843135042761, guid: adacfd07019fd8f4bbd5a9347da9f201, type: 3}
propertyPath: backButton propertyPath: backButton
value: value:
......
...@@ -211,8 +211,7 @@ Transform: ...@@ -211,8 +211,7 @@ Transform:
m_LocalPosition: {x: 0, y: 1, z: -10} m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children: []
- {fileID: 1399870137}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &603037699 --- !u!114 &603037699
...@@ -517,6 +516,7 @@ Transform: ...@@ -517,6 +516,7 @@ Transform:
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
<<<<<<< HEAD
--- !u!1 &1283619807 --- !u!1 &1283619807
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
...@@ -632,6 +632,8 @@ Transform: ...@@ -632,6 +632,8 @@ Transform:
m_CorrespondingSourceObject: {fileID: 6955428004947038011, guid: 81b128416c1c2356c8d645340d865e65, type: 3} m_CorrespondingSourceObject: {fileID: 6955428004947038011, guid: 81b128416c1c2356c8d645340d865e65, type: 3}
m_PrefabInstance: {fileID: 1399870136} m_PrefabInstance: {fileID: 1399870136}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
=======
>>>>>>> origin/pt_refactor_managers
--- !u!1 &1878716305 --- !u!1 &1878716305
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
......
...@@ -23,29 +23,19 @@ namespace com.al_arcade.cs ...@@ -23,29 +23,19 @@ namespace com.al_arcade.cs
Complete, Complete,
} }
public class CsGameManager : MonoBehaviour, IChallengeGame public class CsGameManager : BaseGameManager
{ {
public static CsGameManager Instance { get; private set; } public static CsGameManager Instance { get; private set; }
[Header("Settings")] [Header("Settings")]
[SerializeField] [SerializeField] private float sentenceShowDelay = 0.5f;
private float sentenceShowDelay = 0.5f; [SerializeField] private float feedbackDuration = 1.5f;
[SerializeField] private float wordClickCooldown = 0.3f;
[SerializeField]
private float feedbackDuration = 1.5f;
[SerializeField]
private float wordClickCooldown = 0.3f;
[Header("Arc Settings")] [Header("Arc Settings")]
[SerializeField] [SerializeField] private float arcRadius = 15f;
private float arcRadius = 15f; [SerializeField] private float arcSpanDegrees = 120f;
[SerializeField] private float wordGap = 0.2f;
[SerializeField]
private float arcSpanDegrees = 120f;
[SerializeField]
private float wordGap = 0.2f;
[Header("References")] [Header("References")]
public CsBotController bot; public CsBotController bot;
...@@ -54,158 +44,164 @@ namespace com.al_arcade.cs ...@@ -54,158 +44,164 @@ namespace com.al_arcade.cs
private CsGameState _state = CsGameState.Idle; private CsGameState _state = CsGameState.Idle;
private CsQuestion[] _questions; private CsQuestion[] _questions;
private int _currentIndex;
private int _score,
_streak,
_correctCount,
_wrongCount,
_wrongClicks;
private int _wrongClicks;
private List<CsWordButton> _wordButtons = new(); private List<CsWordButton> _wordButtons = new();
private CsWordButton _wrongWordButton; private CsWordButton _wrongWordButton;
private bool _wordClickLocked; private bool _wordClickLocked;
[Header("Events")] [Header("CS Events")]
public UnityEvent onGameStart;
public UnityEvent<bool> onAnswerGiven;
public UnityEvent<int> onGameComplete; public UnityEvent<int> onGameComplete;
private Action<bool> _onOptionDropped; private Action<bool> _onOptionDropped;
private bool _isTicking;
private float _timeLeft; private int _deltaChangeInSize;
private bool isTicking; private bool _showHint = true;
private DateTime gameStartTime;
// if it reaches 5, you win the game;
int _deltaChangeInSize;
bool showHint = true;
CinemachineTargetGroup _targetGroup; CinemachineTargetGroup _targetGroup;
CinemachineGroupFraming _groupFraming; CinemachineGroupFraming _groupFraming;
public event Action<bool, float> OnGameCompleted; // ─── Singleton ────────────────────────────────────────────────────────
protected override void Awake()
private void Awake()
{
if (Instance != null && Instance != this)
{ {
Destroy(gameObject); if (Instance != null && Instance != this) { Destroy(gameObject); return; }
return;
}
Instance = this; Instance = this;
base.Awake();
_targetGroup = FindFirstObjectByType<CinemachineTargetGroup>(); _targetGroup = FindFirstObjectByType<CinemachineTargetGroup>();
_groupFraming = FindFirstObjectByType<CinemachineGroupFraming>(); _groupFraming = FindFirstObjectByType<CinemachineGroupFraming>();
} }
private void Update() // ─── BaseGameManager implementation ──────────────────────────────────
{ protected override string GameTypeKey => "cs";
if (_state == CsGameState.WaitingForWordClick || _state == CsGameState.WaitingForDrop)
{ protected override IEnumerator FetchQuestions(Action<string> onError)
_timeLeft -= Time.deltaTime;
if (_timeLeft <= 0)
{ {
_timeLeft = 0; var session = SSGameSession.EnsureInstance();
_state = CsGameState.Complete; var api = SSApiManager.EnsureInstance();
StartCoroutine(LoseSequence());
if (uiManager != null && uiManager.isMusicOn)
SSAudioManager.EnsureInstance().PlayMusic();
yield return api.FetchCs(
session.buildType, session.classCode,
session.questionCount, session.gradeId,
qs => _questions = qs,
err => onError(err)
);
} }
if (_timeLeft < 4f && !isTicking) protected override bool HasValidQuestions() =>
_questions != null && _questions.Length > 0;
protected override void OnShowLoading() =>
uiManager?.ShowLoading("جاري تحميل الأسئلة...");
protected override void OnHideLoading() =>
uiManager?.HideLoading();
protected override void OnShowError(string message)
{ {
isTicking = true; uiManager?.ShowError(message);
var audio = SSAudioManager.Instance; _state = CsGameState.Idle;
audio.Tick(true);
} }
else if (_timeLeft >= 4f && isTicking)
protected override IEnumerator OnBeforeBeginGameplay()
{ {
isTicking = false; if (uiManager != null)
var audio = SSAudioManager.Instance; yield return uiManager.ShowCountDown();
audio.Tick(false);
} }
uiManager.SetTimer(_timeLeft); protected override void OnTimerTick(float timeLeft)
{
// Ticking sound when under 4 seconds
if (timeLeft < 4f && !_isTicking)
{
_isTicking = true;
SSAudioManager.Instance?.Tick(true);
} }
else if (timeLeft >= 4f && _isTicking)
{
_isTicking = false;
SSAudioManager.Instance?.Tick(false);
} }
uiManager?.SetTimer(timeLeft);
}
void UpdateTimer(int delta) protected override IEnumerator OnTimeUp()
{ {
_timeLeft += delta; _state = CsGameState.Complete;
_timeLeft = Mathf.Clamp(_timeLeft, 0, CsPrefabBuilder.Instance.startTime); yield return LoseSequence();
}
if (_timeLeft < 4f && !isTicking) protected override void BeginGameplay()
{ {
isTicking = true; _currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
var audio = SSAudioManager.Instance; _deltaChangeInSize = 0;
audio.Tick(true); _timeLeft = CsPrefabBuilder.Instance.startTime; // CS uses its own start time
} _timerRunning = true;
else if (_timeLeft >= 4f && isTicking)
if (uiManager != null)
{ {
isTicking = false; uiManager.ShowGameUI();
var audio = SSAudioManager.Instance; uiManager.SetScore(0);
audio.Tick(false); uiManager.SetProgress(0, _questions.Length);
} }
uiManager.UpdateTimer(_timeLeft, delta > 0); onGameStart?.Invoke();
StartCoroutine(QuestionLoop());
} }
// ─── Update: CS also needs the state guard from original ─────────────
protected override void Update()
{
// Only tick timer when waiting for player input
if (_state == CsGameState.WaitingForWordClick || _state == CsGameState.WaitingForDrop)
base.Update();
}
public void StartGame() => StartCoroutine(StartGameRoutine()); // ─── Public API ───────────────────────────────────────────────────────
public void StartWithQuestions(CsQuestion[] questions) public void StartWithQuestions(CsQuestion[] questions)
{ {
_questions = questions; _questions = questions;
BeginGameplay(); BeginGameplay();
} }
public void ResetGame() public override void ResetGame()
{ {
StopAllCoroutines(); ResetBaseState();
_deltaChangeInSize = 0; _deltaChangeInSize = 0;
_state = CsGameState.Idle; _state = CsGameState.Idle;
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0; _isTicking = false;
ClearWordButtons(); ClearWordButtons();
if (uiManager != null) uiManager?.ResetUI();
uiManager.ResetUI(); bot?.ResetBot();
if (bot != null)
bot.ResetBot();
} }
public void OnWordClicked(CsWordButton wordButton) public void OnWordClicked(CsWordButton wordButton)
{ {
if (_state != CsGameState.WaitingForWordClick) if (_state != CsGameState.WaitingForWordClick) return;
return; if (_wordClickLocked) return;
if (_wordClickLocked)
return;
if (wordButton.IsWrong) if (wordButton.IsWrong)
{ {
_wrongWordButton = wordButton; _wrongWordButton = wordButton;
wordButton.Highlight(true); wordButton.Highlight(true);
var audio = SSAudioManager.Instance; SSAudioManager.Instance?.PlayCorrectChoice();
if (audio != null) SSParticleManager.Instance?.PlaySparks(wordButton.transform.position, SSColorPalette.Accent);
audio.PlayCorrectChoice();
var particles = SSParticleManager.Instance;
if (particles != null)
particles.PlaySparks(wordButton.transform.position, SSColorPalette.Accent);
_state = CsGameState.WaitingForDrop; _state = CsGameState.WaitingForDrop;
ShowOptions(); ShowOptions();
} }
else else
{ {
UpdateTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime); AdjustTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
_wrongClicks++; _wrongClicks++;
_wordClickLocked = true; _wordClickLocked = true;
wordButton.ShakeWrong(); wordButton.ShakeWrong();
SSAudioManager.Instance?.PlayWrongBeep();
var audio = SSAudioManager.Instance;
if (audio != null)
audio.PlayWrongBeep();
StartCoroutine(UnlockAfterCooldown()); StartCoroutine(UnlockAfterCooldown());
} }
} }
...@@ -215,78 +211,34 @@ namespace com.al_arcade.cs ...@@ -215,78 +211,34 @@ namespace com.al_arcade.cs
_onOptionDropped?.Invoke(isCorrectOption); _onOptionDropped?.Invoke(isCorrectOption);
} }
private IEnumerator StartGameRoutine() // ─── Timer helpers ────────────────────────────────────────────────────
{ private void AdjustTimer(float delta)
_state = CsGameState.Loading;
if (uiManager != null)
uiManager.ShowLoading("جاري تحميل الأسئلة...");
var session = SSGameSession.EnsureInstance();
var api = SSApiManager.EnsureInstance();
if (uiManager.isMusicOn)
SSAudioManager.EnsureInstance().PlayMusic();
string error = null;
yield return api.FetchCs(
session.buildType,
session.classCode,
session.questionCount,
session.gradeId,
qs => _questions = qs,
err => error = err
);
if (error != null || _questions == null || _questions.Length == 0)
{
if (uiManager != null)
uiManager.ShowError(error ?? "لا توجد أسئلة");
_state = CsGameState.Idle;
yield break;
}
if (uiManager != null)
uiManager.HideLoading();
yield return uiManager.ShowCountDown();
gameStartTime = DateTime.Now;
BeginGameplay();
}
private void BeginGameplay()
{ {
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0; UpdateTimerBy(delta);
_timeLeft = CsPrefabBuilder.Instance.startTime; _timeLeft = Mathf.Clamp(_timeLeft, 0, CsPrefabBuilder.Instance.startTime);
if (uiManager != null) // Re-check tick state after adjustment
{ if (_timeLeft < 4f && !_isTicking)
uiManager.ShowGameUI(); { _isTicking = true; SSAudioManager.Instance?.Tick(true); }
uiManager.SetScore(0); else if (_timeLeft >= 4f && _isTicking)
uiManager.SetProgress(0, _questions.Length); { _isTicking = false; SSAudioManager.Instance?.Tick(false); }
}
onGameStart?.Invoke(); uiManager?.UpdateTimer(_timeLeft, delta > 0);
StartCoroutine(QuestionLoop());
} }
// ─── Game loop ────────────────────────────────────────────────────────
private IEnumerator QuestionLoop() private IEnumerator QuestionLoop()
{ {
while (_currentIndex < _questions.Length) while (_currentIndex < _questions.Length)
{ {
if (_state == CsGameState.Complete) if (_state == CsGameState.Complete) yield break;
yield break;
yield return PresentQuestion(_questions[_currentIndex]); yield return PresentQuestion(_questions[_currentIndex]);
_currentIndex++; _currentIndex++;
if (uiManager != null) uiManager?.SetProgress(_currentIndex, _questions.Length);
uiManager.SetProgress(_currentIndex, _questions.Length);
} }
if (_state == CsGameState.Complete) if (_state == CsGameState.Complete) yield break;
yield break;
_state = CsGameState.Complete; _state = CsGameState.Complete;
yield return LoseSequence(); yield return LoseSequence();
...@@ -298,9 +250,7 @@ namespace com.al_arcade.cs ...@@ -298,9 +250,7 @@ namespace com.al_arcade.cs
_wrongClicks = 0; _wrongClicks = 0;
_wrongWordButton = null; _wrongWordButton = null;
if (bot != null) bot?.ShowSpeechBubble();
bot.ShowSpeechBubble();
ClearWordButtons(); ClearWordButtons();
yield return new WaitForSeconds(sentenceShowDelay); yield return new WaitForSeconds(sentenceShowDelay);
...@@ -308,8 +258,7 @@ namespace com.al_arcade.cs ...@@ -308,8 +258,7 @@ namespace com.al_arcade.cs
yield return new WaitForSeconds(1.3f); yield return new WaitForSeconds(1.3f);
_state = CsGameState.WaitingForWordClick; _state = CsGameState.WaitingForWordClick;
if (showHint) if (_showHint) uiManager?.ShowHint("انقر على الكلمة الخاطئة في الجملة");
uiManager.ShowHint("انقر على الكلمة الخاطئة في الجملة");
bool questionComplete = false; bool questionComplete = false;
_onOptionDropped = null; _onOptionDropped = null;
...@@ -321,8 +270,7 @@ namespace com.al_arcade.cs ...@@ -321,8 +270,7 @@ namespace com.al_arcade.cs
bool? result = null; bool? result = null;
_onOptionDropped = correct => result = correct; _onOptionDropped = correct => result = correct;
while (result == null) while (result == null) yield return null;
yield return null;
if (result == true) if (result == true)
{ {
...@@ -340,8 +288,7 @@ namespace com.al_arcade.cs ...@@ -340,8 +288,7 @@ namespace com.al_arcade.cs
} }
} }
if (uiManager != null) uiManager?.HideOptions();
uiManager.HideOptions();
yield return new WaitForSeconds(0.5f); yield return new WaitForSeconds(0.5f);
} }
...@@ -351,12 +298,10 @@ namespace com.al_arcade.cs ...@@ -351,12 +298,10 @@ namespace com.al_arcade.cs
_correctCount++; _correctCount++;
_streak++; _streak++;
_deltaChangeInSize++; _deltaChangeInSize++;
UpdateTimer(CsPrefabBuilder.Instance.correctAnswerBonusTime); AdjustTimer(CsPrefabBuilder.Instance.correctAnswerBonusTime);
int points = Mathf.Max(100 - _wrongClicks * 15, 25); int points = Mathf.Max(100 - _wrongClicks * 15, 25);
if (_streak >= 3) if (_streak >= 3) points += (_streak - 2) * 25;
points += (_streak - 2) * 25;
_score += points; _score += points;
if (_wrongWordButton != null) if (_wrongWordButton != null)
...@@ -372,26 +317,20 @@ namespace com.al_arcade.cs ...@@ -372,26 +317,20 @@ namespace com.al_arcade.cs
SSAudioManager.Instance.PlayCorrectDrag(); SSAudioManager.Instance.PlayCorrectDrag();
yield return new WaitForSeconds(0.4f); yield return new WaitForSeconds(0.4f);
if (bot != null) bot?.PlayHappy();
bot.PlayHappy(); uiManager?.HideHint();
uiManager.HideHint(); if (SSParticleManager.Instance != null && _wrongWordButton != null)
SSParticleManager.Instance.PlayCorrectBurst(_wrongWordButton.transform.position);
var particles = SSParticleManager.Instance;
if (particles != null && _wrongWordButton != null)
particles.PlayCorrectBurst(_wrongWordButton.transform.position);
uiManager.TickPoints(_deltaChangeInSize);
uiManager?.TickPoints(_deltaChangeInSize);
onAnswerGiven?.Invoke(true); onAnswerGiven?.Invoke(true);
yield return new WaitForSeconds(0.8f); yield return new WaitForSeconds(0.8f);
if (_deltaChangeInSize == 5) if (_deltaChangeInSize == 5)
{ {
// Win
_state = CsGameState.Complete; _state = CsGameState.Complete;
yield return VictorySequence(); yield return VictorySequence();
yield break;
} }
} }
...@@ -399,12 +338,9 @@ namespace com.al_arcade.cs ...@@ -399,12 +338,9 @@ namespace com.al_arcade.cs
{ {
_wrongClicks++; _wrongClicks++;
_streak = 0; _streak = 0;
_deltaChangeInSize = Mathf.Max(0, _deltaChangeInSize - 1);
_deltaChangeInSize--; AdjustTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
if (_deltaChangeInSize < 0)
_deltaChangeInSize = 0;
UpdateTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
if (uiManager != null) if (uiManager != null)
{ {
...@@ -415,8 +351,7 @@ namespace com.al_arcade.cs ...@@ -415,8 +351,7 @@ namespace com.al_arcade.cs
SSAudioManager.Instance.PlayWrong(); SSAudioManager.Instance.PlayWrong();
yield return new WaitForSeconds(0.4f); yield return new WaitForSeconds(0.4f);
if (bot != null) bot?.PlaySad();
bot.PlaySad();
if (Camera.main != null) if (Camera.main != null)
{ {
...@@ -424,30 +359,60 @@ namespace com.al_arcade.cs ...@@ -424,30 +359,60 @@ namespace com.al_arcade.cs
Camera.main.transform.DOShakePosition(0.3f, 0.15f, 10).SetId("csShake"); Camera.main.transform.DOShakePosition(0.3f, 0.15f, 10).SetId("csShake");
} }
uiManager.TickPoints(_deltaChangeInSize); uiManager?.TickPoints(_deltaChangeInSize);
yield return new WaitForSeconds(0.8f); yield return new WaitForSeconds(0.8f);
} }
private void SpawnWords(CsQuestion question) // ─── End sequences ────────────────────────────────────────────────────
protected override IEnumerator SharedVictorySequence()
{
var audio = SSAudioManager.Instance;
audio.PlayVictory();
StartCoroutine(DelayedAction(0.7f, () => audio.PlayCheer())); // shared helper
SSParticleManager.Instance?.PlayScreenConfetti();
bot?.PlayVictoryDance();
yield return new WaitForSeconds(1f);
SSAudioManager.Instance.StopMusic();
}
protected override IEnumerator NoChallengeVictorySequence()
{ {
var fullQuestion = question.words != null yield return new WaitForSeconds(1f);
? string.Join(" ", Array.ConvertAll(question.words, w => w.word_text)) uiManager?.ShowResults(_score, _correctCount, _wrongClicks, _questions.Length, _streak);
: "";
if (question.words == null || question.words.Length == 0) onGameComplete?.Invoke(_score);
return; ClearWordButtons();
}
protected override IEnumerator NoChallengeLoseSequence()
{
uiManager?.ShowResults(_score, _correctCount, _wrongClicks, _questions.Length, _streak, false);
yield return null;
}
protected override IEnumerator SharedLoseSequence()
{
SSAudioManager.Instance.PlayDefeat();
onGameComplete?.Invoke(_score);
SSAudioManager.Instance.StopMusic();
ClearWordButtons();
yield return null;
}
// ─── Word / UI helpers ────────────────────────────────────────────────
private void SpawnWords(CsQuestion question)
{
if (question.words == null || question.words.Length == 0) return;
ClearWordButtons(); ClearWordButtons();
_groupFraming.CenterOffset = new Vector2(0f, -0.1f); _groupFraming.CenterOffset = new Vector2(0f, -0.1f);
// Spawn Full Sentence
var sentenceObj = new GameObject("FullQuestion"); var sentenceObj = new GameObject("FullQuestion");
sentenceObj.transform.SetParent(wordContainer); sentenceObj.transform.SetParent(wordContainer);
var sentenceWb = sentenceObj.AddComponent<CsSentence>(); var sentenceWb = sentenceObj.AddComponent<CsSentence>();
sentenceWb.Initialize(question); sentenceWb.Initialize(question);
return;
} }
private void ShowOptions() private void ShowOptions()
...@@ -456,10 +421,10 @@ namespace com.al_arcade.cs ...@@ -456,10 +421,10 @@ namespace com.al_arcade.cs
if (uiManager != null) if (uiManager != null)
{ {
uiManager.ShowOptions(question.options, _wrongWordButton); uiManager.ShowOptions(question.options, _wrongWordButton);
if (showHint) if (_showHint)
{ {
uiManager.ShowHint("اسحب الإجابة الصحيحة إلى الكلمة الخاطئة"); uiManager.ShowHint("اسحب الإجابة الصحيحة إلى الكلمة الخاطئة");
showHint = false; _showHint = false;
} }
} }
} }
...@@ -468,10 +433,8 @@ namespace com.al_arcade.cs ...@@ -468,10 +433,8 @@ namespace com.al_arcade.cs
{ {
foreach (Transform child in wordContainer) foreach (Transform child in wordContainer)
{ {
child.transform.DOScale(Vector3.zero, 0.3f).SetEase(Ease.InBack).OnComplete(() => child.transform.DOScale(Vector3.zero, 0.3f).SetEase(Ease.InBack)
{ .OnComplete(() => Destroy(child.gameObject));
Destroy(child.gameObject);
});
} }
if (_targetGroup.Targets.Count > 2) if (_targetGroup.Targets.Count > 2)
...@@ -490,91 +453,5 @@ namespace com.al_arcade.cs ...@@ -490,91 +453,5 @@ namespace com.al_arcade.cs
_wordClickLocked = false; _wordClickLocked = false;
} }
private IEnumerator VictorySequence()
{
var audio = SSAudioManager.Instance;
audio.PlayVictory();
StartCoroutine(DelayedAction(0.7f, () =>
{
audio.PlayCheer();
}));
var particles = SSParticleManager.Instance;
if (particles != null)
particles.PlayScreenConfetti();
if (bot != null)
bot.PlayVictoryDance();
yield return new WaitForSeconds(1f);
if (uiManager != null)
uiManager.ShowResults(
_score,
_correctCount,
_wrongClicks,
_questions.Length,
_streak
);
onGameComplete?.Invoke(_score);
SSAudioManager.Instance.StopMusic();
ClearWordButtons();
OnGameCompleted?.Invoke(true, _timeLeft);
yield return new WaitForSeconds(5f);
SceneManager.LoadScene("App");
}
private IEnumerator DelayedAction(float delay, Action action)
{
yield return new WaitForSeconds(delay);
action?.Invoke();
}
private IEnumerator LoseSequence()
{
var audio = SSAudioManager.Instance;
audio.PlayDefeat();
if (uiManager != null)
uiManager.ShowResults(
_score,
_correctCount,
_wrongClicks,
_questions.Length,
_streak,
false
);
OnGameCompleted?.Invoke(false, _timeLeft);
onGameComplete?.Invoke(_score);
SSAudioManager.Instance.StopMusic();
ClearWordButtons();
OnGameCompleted?.Invoke(false, _timeLeft);
yield return null;
} }
public int Score => _score;
private ChallengeManager _challengeManager;
private bool _challengeChecked;
public bool IsChallengeMode
{
get
{
if (!_challengeChecked)
{
_challengeManager = FindFirstObjectByType<ChallengeManager>();
_challengeChecked = true;
}
return _challengeManager != null;
}
}
}
} }
\ No newline at end of file
...@@ -227,8 +227,11 @@ namespace com.al_arcade.cs ...@@ -227,8 +227,11 @@ namespace com.al_arcade.cs
if (useOfflineTestData) if (useOfflineTestData)
_gm.StartWithQuestions(GetTestQuestions()); _gm.StartWithQuestions(GetTestQuestions());
else else
{
if (!_gm.IsChallengeMode)
_gm.StartGame(); _gm.StartGame();
} }
}
private void SetupCamera() private void SetupCamera()
......
using DG.Tweening;
using UnityEngine;
public class FutureCar : MonoBehaviour
{
[Header("Main Settings")]
[Tooltip("Check to float in place, uncheck to move back and forth.")]
public bool isFloatingInPlace = true;
[Header("Floating Options (Only used if checked above)")]
public float floatSpeed = 1f;
public float floatAmplitude = 0.5f;
[Header("Moving Options (Only used if unchecked above)")]
public float moveSpeed = 5f;
public float travelDistance = 20f;
public float rotationSpeed = 3f;
private Vector3 startPosition;
private Vector3 targetPosition;
private bool movingToTarget = true;
private float initialY;
void Start()
{
// Store the starting spot
startPosition = transform.position;
initialY = transform.position.y;
// Calculate the end of the patrol path based on the car's initial forward direction
targetPosition = startPosition + (transform.forward * travelDistance);
ApplyFloatingMotion();
}
// void Update()
// {
// if (isFloatingInPlace)
// {
// ApplyFloatingMotion();
// }
// else
// {
// ApplyPatrolMotion();
// }
// }
private void ApplyFloatingMotion()
{
var randomDelay = Random.Range(0f, 1f);
transform.DOMoveY(initialY + floatAmplitude, floatSpeed)
.SetEase(Ease.InOutSine)
.SetDelay(randomDelay)
.SetLoops(-1, LoopType.Yoyo);
}
private void ApplyPatrolMotion()
{
// Determine which end of the line we are headed to
Vector3 destination = movingToTarget ? targetPosition : startPosition;
// 1. Move the car
transform.position = Vector3.MoveTowards(transform.position, destination, moveSpeed * Time.deltaTime);
// 2. Rotate the car to face the current destination
Vector3 direction = (destination - transform.position).normalized;
if (direction != Vector3.zero)
{
Quaternion lookRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * rotationSpeed);
}
// 3. If we are very close to the destination, flip the boolean to go the other way
if (Vector3.Distance(transform.position, destination) < 0.1f)
{
movingToTarget = !movingToTarget;
}
}
// This helps you see the path in the Scene View without pressing play
private void OnDrawGizmosSelected()
{
if (!isFloatingInPlace)
{
Gizmos.color = Color.cyan;
Vector3 endPoint = Application.isPlaying ? targetPosition : transform.position + (transform.forward * travelDistance);
Vector3 startPoint = Application.isPlaying ? startPosition : transform.position;
Gizmos.DrawLine(startPoint, endPoint);
Gizmos.DrawWireSphere(endPoint, 0.5f);
}
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive; using com.al_arcade.shared;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using EasyTransition; using EasyTransition;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
// Spawns three games one after the other. // Spawns three games one after the other.
// Move to next game when the current game is won, if lost end the challenge and show results. // Move to next game when the current game is won, if lost end the challenge and show results.
...@@ -26,6 +25,7 @@ public class ChallengeManager : MonoBehaviour ...@@ -26,6 +25,7 @@ public class ChallengeManager : MonoBehaviour
private DateTime startTime; private DateTime startTime;
IChallengeGame currentGame = null; IChallengeGame currentGame = null;
BaseGameManager baseGameManager = null;
[SerializeField] private ChallengeCanvas challengeCanvas; [SerializeField] private ChallengeCanvas challengeCanvas;
...@@ -50,7 +50,7 @@ public class ChallengeManager : MonoBehaviour ...@@ -50,7 +50,7 @@ public class ChallengeManager : MonoBehaviour
[ContextMenu("Fake win")] [ContextMenu("Fake win")]
public void Fakewin() public void Fakewin()
{ {
OnGameCompleted(true, 30); OnGameCompleted(true, 30, 100);
} }
...@@ -64,8 +64,9 @@ public class ChallengeManager : MonoBehaviour ...@@ -64,8 +64,9 @@ public class ChallengeManager : MonoBehaviour
await LoadNextGameAndListen(); await LoadNextGameAndListen();
} }
private void OnGameCompleted(bool hasWon, float timeLeft) private void OnGameCompleted(bool hasWon, float timeLeft, float pointsEarned)
{ {
print("Game completed");
if (currentGame != null) if (currentGame != null)
currentGame.OnGameCompleted -= OnGameCompleted; currentGame.OnGameCompleted -= OnGameCompleted;
...@@ -100,6 +101,12 @@ public class ChallengeManager : MonoBehaviour ...@@ -100,6 +101,12 @@ public class ChallengeManager : MonoBehaviour
return currentGame != null; return currentGame != null;
}); });
baseGameManager = currentGame as BaseGameManager;
await UniTask.WaitForSeconds(0.5f);
baseGameManager.ResetGame();
baseGameManager.StartGame();
currentGame.OnGameCompleted += OnGameCompleted; currentGame.OnGameCompleted += OnGameCompleted;
} }
...@@ -108,7 +115,7 @@ public class ChallengeManager : MonoBehaviour ...@@ -108,7 +115,7 @@ public class ChallengeManager : MonoBehaviour
Debug.Log("Challenge failed."); Debug.Log("Challenge failed.");
// Show results, reset challenge, etc. // Show results, reset challenge, etc.
challengeCanvas.ShowChallengeResult(false, timeSaved, -penaltiesPerGame[currentGameIndex]); challengeCanvas.ShowChallengeResult(false, 0, -penaltiesPerGame[currentGameIndex]);
await ChallengeService.Instance.AddChallenge(false, 0, startTime, DateTime.UtcNow); await ChallengeService.Instance.AddChallenge(false, 0, startTime, DateTime.UtcNow);
} }
......
...@@ -3,5 +3,5 @@ using System; ...@@ -3,5 +3,5 @@ using System;
public interface IChallengeGame public interface IChallengeGame
{ {
bool IsChallengeMode { get; } bool IsChallengeMode { get; }
event Action<bool, float> OnGameCompleted; // hasWon, timeLeft event Action<bool, float, float> OnGameCompleted; // hasWon, timeLeft
} }
\ No newline at end of file
...@@ -15,11 +15,11 @@ namespace com.al_arcade.mcq ...@@ -15,11 +15,11 @@ namespace com.al_arcade.mcq
AnswerFeedback, BetweenQuestions, GameComplete, GameOver AnswerFeedback, BetweenQuestions, GameComplete, GameOver
} }
public class McqGameManager : MonoBehaviour public class McqGameManager : BaseGameManager
{ {
public static McqGameManager Instance { get; private set; } public static McqGameManager Instance { get; private set; }
[Header("Game Settings")] [Header("MCQ Settings")]
[SerializeField] private float gateSpawnDistance = 50f; [SerializeField] private float gateSpawnDistance = 50f;
[SerializeField] private float questionDisplayTime = 1.5f; [SerializeField] private float questionDisplayTime = 1.5f;
[SerializeField] private float feedbackDisplayTime = 2f; [SerializeField] private float feedbackDisplayTime = 2f;
...@@ -40,101 +40,95 @@ namespace com.al_arcade.mcq ...@@ -40,101 +40,95 @@ namespace com.al_arcade.mcq
[SerializeField] private McqGameState _state = McqGameState.Idle; [SerializeField] private McqGameState _state = McqGameState.Idle;
public McqGameState State => _state; public McqGameState State => _state;
// MCQ-specific state
private McqQuestion[] _questions; private McqQuestion[] _questions;
private int _currentIndex; private int _bestStreak, _lives;
private int _score, _streak, _bestStreak, _lives;
private int _correctCount, _wrongCount;
private List<McqGateController> _activeGates = new(); private List<McqGateController> _activeGates = new();
private int _correctGateIndex = -1; private int _correctGateIndex = -1;
private Camera _mainCamera; private Camera _mainCamera;
[Header("Events")] [Header("MCQ Events")]
public UnityEvent onGameStart;
public UnityEvent<McqQuestion> onQuestionPresented; public UnityEvent<McqQuestion> onQuestionPresented;
public UnityEvent<bool> onAnswerGiven = new UnityEvent<bool>();
public UnityEvent<int, int> onScoreChanged; public UnityEvent<int, int> onScoreChanged;
public UnityEvent<int> onLifeLost; public UnityEvent<int> onLifeLost;
public UnityEvent<int> onGameComplete; public UnityEvent<int> onGameComplete;
public UnityEvent onGameOver = new UnityEvent(); public UnityEvent onGameOver = new UnityEvent();
private void Awake() // ─── Singleton ────────────────────────────────────────────────────────
protected override void Awake()
{ {
if (Instance != null && Instance != this) { Destroy(gameObject); return; } if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this; Instance = this;
base.Awake();
_mainCamera = Camera.main; _mainCamera = Camera.main;
onAnswerGiven.AddListener(CameraFeedback); onAnswerGiven.AddListener(CameraFeedback);
} }
public void StartGame() => StartCoroutine(StartGameRoutine()); // ─── BaseGameManager implementation ──────────────────────────────────
protected override string GameTypeKey => "mcq";
public void StartWithQuestions(McqQuestion[] questions)
{
_questions = questions;
BeginGameplay();
}
public void ResetGame()
{
StopAllCoroutines();
_state = McqGameState.Idle;
_currentIndex = 0;
_score = _streak = _bestStreak = _correctCount = _wrongCount = 0;
_lives = totalLives;
_questions = null;
foreach (var g in _activeGates)
if (g != null) { DOTween.Kill(g.transform); Destroy(g.gameObject); }
_activeGates.Clear();
if (player != null) player.ResetToStart();
if (uiManager != null) uiManager.ResetUI();
if (questionDisplay != null) questionDisplay.Hide();
}
private IEnumerator StartGameRoutine() protected override IEnumerator FetchQuestions(Action<string> onError)
{ {
player?.SetupManager(this);
competitor?.SetupManager(this);
_state = McqGameState.Loading;
if (uiManager != null) uiManager.ShowLoading("جاري تحميل الأسئلة...");
var session = SSGameSession.EnsureInstance(); var session = SSGameSession.EnsureInstance();
var api = SSApiManager.EnsureInstance(); var api = SSApiManager.EnsureInstance();
string error = null;
yield return api.FetchMcq( yield return api.FetchMcq(
session.buildType, session.classCode, session.buildType, session.classCode,
session.questionCount, session.gradeId, session.questionCount, session.gradeId,
qs => _questions = qs, qs => _questions = qs,
err => error = err err => onError(err)
); );
}
protected override bool HasValidQuestions() =>
_questions != null && _questions.Length > 0;
if (error != null || _questions == null || _questions.Length == 0) protected override void OnShowLoading() =>
uiManager?.ShowLoading("جاري تحميل الأسئلة...");
protected override void OnHideLoading() =>
uiManager?.HideLoading();
protected override void OnShowError(string message)
{ {
if (uiManager != null) uiManager.ShowError(error ?? "لا توجد أسئلة متاحة"); uiManager?.ShowError(message);
_state = McqGameState.Idle; _state = McqGameState.Idle;
yield break;
} }
if (uiManager != null) uiManager.HideLoading(); protected override IEnumerator OnBeforeBeginGameplay()
{
player?.SetupManager(this);
competitor?.SetupManager(this);
// Robot getting up animation // Robot getting up animation
yield return new WaitForSeconds(1.5f); yield return new WaitForSeconds(1.5f);
}
BeginGameplay(); protected override void OnTimerTick(float timeLeft)
{
// MCQ doesn't use a global timer - it uses lives
}
protected override IEnumerator OnTimeUp()
{
// MCQ doesn't use a timer
yield break;
}
// MCQ doesn't use the base timer - disable it
protected override void Update()
{
// Don't call base.Update() - MCQ uses lives, not a timer
} }
private void BeginGameplay() // ─── BeginGameplay ───────────────────────────────────────────────────
protected override void BeginGameplay()
{ {
_currentIndex = 0; _currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
_score = _streak = _bestStreak = _correctCount = _wrongCount = 0; _bestStreak = 0;
_lives = totalLives; _lives = totalLives;
_timerRunning = false; // MCQ doesn't use timer
if (uiManager != null) if (uiManager != null)
{ {
...@@ -144,12 +138,39 @@ namespace com.al_arcade.mcq ...@@ -144,12 +138,39 @@ namespace com.al_arcade.mcq
uiManager.ShowGameUI(); uiManager.ShowGameUI();
} }
questionDisplay.transform.DOScale(Vector3.one, 0.5f).From(Vector3.zero).SetEase(Ease.OutBack); questionDisplay.transform.DOScale(Vector3.one, 0.5f)
.From(Vector3.zero)
.SetEase(Ease.OutBack);
onGameStart?.Invoke(); onGameStart?.Invoke();
StartCoroutine(GameLoop()); StartCoroutine(GameLoop());
} }
// ─── Public API ───────────────────────────────────────────────────────
public void StartWithQuestions(McqQuestion[] questions)
{
_questions = questions;
BeginGameplay();
}
public void ResetGame()
{
ResetBaseState();
_state = McqGameState.Idle;
_bestStreak = 0;
_lives = totalLives;
_questions = null;
foreach (var g in _activeGates)
if (g != null) { DOTween.Kill(g.transform); Destroy(g.gameObject); }
_activeGates.Clear();
if (player != null) player.ResetToStart();
if (uiManager != null) uiManager.ResetUI();
if (questionDisplay != null) questionDisplay.Hide();
}
// ─── Game Loop ───────────────────────────────────────────────────────
private IEnumerator GameLoop() private IEnumerator GameLoop()
{ {
while (_currentIndex < _questions.Length && _lives > 0) while (_currentIndex < _questions.Length && _lives > 0)
...@@ -163,7 +184,7 @@ namespace com.al_arcade.mcq ...@@ -163,7 +184,7 @@ namespace com.al_arcade.mcq
if (_lives <= 0) if (_lives <= 0)
{ {
_state = McqGameState.GameOver; _state = McqGameState.GameOver;
yield return GameOverSequence(); yield return LoseSequence();
} }
else else
{ {
...@@ -181,7 +202,6 @@ namespace com.al_arcade.mcq ...@@ -181,7 +202,6 @@ namespace com.al_arcade.mcq
questionDisplay.Show(question.question_text, question.source); questionDisplay.Show(question.question_text, question.source);
onQuestionPresented?.Invoke(question); onQuestionPresented?.Invoke(question);
// yield return new WaitForSeconds(questionDisplayTime);
_state = McqGameState.WaitingForAnswer; _state = McqGameState.WaitingForAnswer;
...@@ -210,18 +230,12 @@ namespace com.al_arcade.mcq ...@@ -210,18 +230,12 @@ namespace com.al_arcade.mcq
yield return null; yield return null;
} }
/*
if (player != null) player.Stop();
if (competitor != null) competitor.Stop();
*/
foreach (var gate in _activeGates) foreach (var gate in _activeGates)
gate.onPlayerEnter -= OnGateHit; gate.onPlayerEnter -= OnGateHit;
_state = McqGameState.AnswerFeedback; _state = McqGameState.AnswerFeedback;
yield return ProcessAnswer(answered && wasCorrect); yield return ProcessAnswer(answered && wasCorrect);
foreach (var gate in _activeGates) foreach (var gate in _activeGates)
{ {
if (gate != null) if (gate != null)
...@@ -236,7 +250,6 @@ namespace com.al_arcade.mcq ...@@ -236,7 +250,6 @@ namespace com.al_arcade.mcq
yield return new WaitForSeconds(0.5f); yield return new WaitForSeconds(0.5f);
_state = McqGameState.BetweenQuestions; _state = McqGameState.BetweenQuestions;
//if (player != null) yield return player.MoveToNextLane(0.4f);
if (questionDisplay != null) questionDisplay.Hide(); if (questionDisplay != null) questionDisplay.Hide();
} }
...@@ -247,9 +260,7 @@ namespace com.al_arcade.mcq ...@@ -247,9 +260,7 @@ namespace com.al_arcade.mcq
_correctCount++; _correctCount++;
_streak++; _streak++;
if (_streak > _bestStreak) _bestStreak = _streak; if (_streak > _bestStreak) _bestStreak = _streak;
int points = 100; int points = CalculateStreakScore((int)streakBonusThreshold);
if (_streak >= streakBonusThreshold)
points += (_streak - (int)streakBonusThreshold + 1) * 25;
_score += points; _score += points;
ShowCorrectFeedback(points); ShowCorrectFeedback(points);
...@@ -260,11 +271,11 @@ namespace com.al_arcade.mcq ...@@ -260,11 +271,11 @@ namespace com.al_arcade.mcq
if (audio.sfxCorrect != null) audio.PlayCorrect(); if (audio.sfxCorrect != null) audio.PlayCorrect();
else audio.PlayCorrectBeep(); else audio.PlayCorrectBeep();
} }
var particles = SSParticleManager.Instance; var particles = SSParticleManager.Instance;
if (particles != null && player != null) if (particles != null && player != null)
particles.PlayCorrectBurst(player.transform.position + Vector3.up * 2f); particles.PlayCorrectBurst(player.transform.position + Vector3.up * 2f);
// Sky color
Camera.main.DOColor(SSColorPalette.CorrectWord, 1).SetEase(Ease.Flash, 2); Camera.main.DOColor(SSColorPalette.CorrectWord, 1).SetEase(Ease.Flash, 2);
} }
else else
...@@ -280,6 +291,7 @@ namespace com.al_arcade.mcq ...@@ -280,6 +291,7 @@ namespace com.al_arcade.mcq
if (audio.sfxWrong != null) audio.PlayWrong(); if (audio.sfxWrong != null) audio.PlayWrong();
else audio.PlayWrongBeep(); else audio.PlayWrongBeep();
} }
var particles = SSParticleManager.Instance; var particles = SSParticleManager.Instance;
if (particles != null && player != null) if (particles != null && player != null)
particles.PlayWrongBurst(player.transform.position + Vector3.up * 2f); particles.PlayWrongBurst(player.transform.position + Vector3.up * 2f);
...@@ -287,7 +299,6 @@ namespace com.al_arcade.mcq ...@@ -287,7 +299,6 @@ namespace com.al_arcade.mcq
if (uiManager != null) uiManager.SetLives(_lives, totalLives); if (uiManager != null) uiManager.SetLives(_lives, totalLives);
onLifeLost?.Invoke(_lives); onLifeLost?.Invoke(_lives);
// Sky color
Camera.main.DOColor(SSColorPalette.WrongWord, 1).SetEase(Ease.Flash, 2); Camera.main.DOColor(SSColorPalette.WrongWord, 1).SetEase(Ease.Flash, 2);
} }
...@@ -297,7 +308,7 @@ namespace com.al_arcade.mcq ...@@ -297,7 +308,7 @@ namespace com.al_arcade.mcq
yield return new WaitForSeconds(feedbackDisplayTime); yield return new WaitForSeconds(feedbackDisplayTime);
} }
// ─── Gate Spawning ───────────────────────────────────────────────────
private void SpawnGates(McqQuestion question) private void SpawnGates(McqQuestion question)
{ {
string[] answers = question.GetShuffledAnswers(out int correctIdx); string[] answers = question.GetShuffledAnswers(out int correctIdx);
...@@ -308,7 +319,6 @@ namespace com.al_arcade.mcq ...@@ -308,7 +319,6 @@ namespace com.al_arcade.mcq
questionDisplay.ShowAnswers(answers); questionDisplay.ShowAnswers(answers);
float playerZ = player != null ? player.transform.position.z : 0f; float playerZ = player != null ? player.transform.position.z : 0f;
Vector3 basePos = new Vector3(0f, 0f, playerZ + gateSpawnDistance); Vector3 basePos = new Vector3(0f, 0f, playerZ + gateSpawnDistance);
float totalWidth = (answers.Length - 1) * gateSpacing; float totalWidth = (answers.Length - 1) * gateSpacing;
...@@ -347,19 +357,15 @@ namespace com.al_arcade.mcq ...@@ -347,19 +357,15 @@ namespace com.al_arcade.mcq
McqGateController gate; McqGateController gate;
if (gatePrefab == null) if (gatePrefab == null)
{
gate = go.AddComponent<McqGateController>(); gate = go.AddComponent<McqGateController>();
}
else else
{
gate = go.GetComponent<McqGateController>(); gate = go.GetComponent<McqGateController>();
}
gate.Setup(index, answerText, isCorrect); gate.Setup(index, answerText, isCorrect);
return gate; return gate;
} }
// ─── Feedback ────────────────────────────────────────────────────────
private void ShowCorrectFeedback(int points) private void ShowCorrectFeedback(int points)
{ {
if (uiManager != null) if (uiManager != null)
...@@ -369,6 +375,7 @@ namespace com.al_arcade.mcq ...@@ -369,6 +375,7 @@ namespace com.al_arcade.mcq
: $"صحيح! +{points}"; : $"صحيح! +{points}";
uiManager.ShowFeedback(msg, true); uiManager.ShowFeedback(msg, true);
} }
foreach (var g in _activeGates) foreach (var g in _activeGates)
{ {
if (g == null) continue; if (g == null) continue;
...@@ -412,16 +419,30 @@ namespace com.al_arcade.mcq ...@@ -412,16 +419,30 @@ namespace com.al_arcade.mcq
} }
} }
private IEnumerator VictorySequence() private void CameraFeedback(bool correct)
{ {
Debug.Log("victory sequence"); _mainCamera.DOFieldOfView(correct ? 70f : 60f, 0.2f).SetEase(Ease.OutQuad);
}
// ─── End Sequences ───────────────────────────────────────────────────
protected override IEnumerator SharedVictorySequence()
{
Debug.Log("MCQ victory sequence");
var audio = SSAudioManager.Instance; var audio = SSAudioManager.Instance;
if (audio != null) if (audio != null)
{ if (audio.sfxVictory != null) audio.PlayVictory(); else audio.PlaySuccessJingle(); } {
if (audio.sfxVictory != null) audio.PlayVictory();
else audio.PlaySuccessJingle();
}
var particles = SSParticleManager.Instance; var particles = SSParticleManager.Instance;
if (particles != null) particles.PlayScreenConfetti(); if (particles != null) particles.PlayScreenConfetti();
yield return new WaitForSeconds(1f);
}
protected override IEnumerator NoChallengeVictorySequence()
{
yield return new WaitForSeconds(0.5f); yield return new WaitForSeconds(0.5f);
if (uiManager != null) if (uiManager != null)
uiManager.ShowResults(_score, _correctCount, _wrongCount, uiManager.ShowResults(_score, _correctCount, _wrongCount,
...@@ -429,13 +450,21 @@ namespace com.al_arcade.mcq ...@@ -429,13 +450,21 @@ namespace com.al_arcade.mcq
onGameComplete?.Invoke(_score); onGameComplete?.Invoke(_score);
} }
private IEnumerator GameOverSequence() protected override IEnumerator SharedLoseSequence()
{ {
Debug.Log("Game over"); Debug.Log("MCQ game over");
var audio = SSAudioManager.Instance; var audio = SSAudioManager.Instance;
if (audio != null) if (audio != null)
{ if (audio.sfxDefeat != null) audio.PlayDefeat(); else audio.PlayFailBuzz(); } {
if (audio.sfxDefeat != null) audio.PlayDefeat();
else audio.PlayFailBuzz();
}
yield return new WaitForSeconds(0.5f);
}
protected override IEnumerator NoChallengeLoseSequence()
{
yield return new WaitForSeconds(0.5f); yield return new WaitForSeconds(0.5f);
if (uiManager != null) if (uiManager != null)
uiManager.ShowResults(_score, _correctCount, _wrongCount, uiManager.ShowResults(_score, _correctCount, _wrongCount,
...@@ -443,16 +472,9 @@ namespace com.al_arcade.mcq ...@@ -443,16 +472,9 @@ namespace com.al_arcade.mcq
onGameOver?.Invoke(); onGameOver?.Invoke();
} }
private void CameraFeedback(bool correct) // ─── MCQ-specific accessors ──────────────────────────────────────────
{
// If Correct Increase fov
_mainCamera.DOFieldOfView(correct ? 70f : 60f, 0.2f).SetEase(Ease.OutQuad);
}
public int Score => _score;
public int Lives => _lives; public int Lives => _lives;
public int Streak => _streak; public int BestStreak => _bestStreak;
public int CurrentQuestionIndex => _currentIndex;
public int TotalQuestions => _questions?.Length ?? 0; public int TotalQuestions => _questions?.Length ?? 0;
} }
} }
\ No newline at end of file
using System;
using System.Collections;
using ExternPropertyAttributes;
using UnityEngine;
using UnityEngine.Events;
namespace com.al_arcade.shared
{
/// <summary>
/// Abstract base class for all game managers (TF, MCQ, CS, ...).
/// Handles: singleton wiring, score/streak/counts, timer, API loading
/// skeleton, game history recording, and shared UnityEvents.
///
/// Subclasses MUST:
/// - Declare their own static Instance property
/// - Call base.Awake() (or replicate the guard) in their own Awake()
/// - Implement all abstract members
/// </summary>
public abstract class BaseGameManager : MonoBehaviour, IChallengeGame
{
// ─── Timer ───────────────────────────────────────────────────────────
[Header("Timer Settings")]
[SerializeField] protected float maxTimePerQuestion = 30f;
protected float _timeLeft;
protected bool _timerRunning;
// ─── Shared runtime state ────────────────────────────────────────────
protected int _score;
protected int _streak;
protected int _correctCount;
protected int _wrongCount;
protected int _currentIndex;
protected int _totalAsked;
protected DateTime gameStartTime;
// ─── Shared UnityEvents ──────────────────────────────────────────────
[Header("Base Events")]
public UnityEvent onGameStart;
public UnityEvent<bool> onAnswerGiven;
// ─────────────────────────────────────────────────────────────────────
// Abstract API – every subclass must implement these
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// Fetch questions from the API and store them internally.
/// Yield inside to wait for the network call.
/// Set 'error' to a non-null string if something goes wrong.
/// </summary>
protected abstract IEnumerator FetchQuestions(Action<string> onError);
/// <summary>
/// Returns true if the fetch result is considered valid
/// (e.g. questions array is non-null and non-empty).
/// </summary>
protected abstract bool HasValidQuestions();
/// <summary>Called after questions load successfully — start your game loop here.</summary>
protected abstract void BeginGameplay();
/// <summary>Called when the countdown timer hits zero.</summary>
protected abstract IEnumerator OnTimeUp();
/// <summary>
/// The game-type key passed to GameHistoryService (e.g. "tf", "mcq", "cs").
/// </summary>
protected abstract string GameTypeKey { get; }
// ─────────────────────────────────────────────────────────────────────
// Singleton wiring (subclass keeps its own typed static Instance)
// ─────────────────────────────────────────────────────────────────────
protected virtual void Awake()
{
// Subclasses that need extra Awake logic should call base.Awake()
// THEN do their own work.
}
// ─────────────────────────────────────────────────────────────────────
// Timer
// ─────────────────────────────────────────────────────────────────────
protected virtual void Update()
{
if (!_timerRunning) return;
_timeLeft -= Time.deltaTime;
OnTimerTick(_timeLeft);
if (_timeLeft <= 0f)
{
_timeLeft = 0f;
_timerRunning = false;
StartCoroutine(OnTimeUp());
}
}
/// <summary>
/// Called every frame while the timer is running.
/// Override to push the value to your UI manager.
/// </summary>
protected virtual void OnTimerTick(float timeLeft) { }
/// <summary>Add or subtract time and clamp to [0, maxTimePerQuestion].</summary>
protected void UpdateTimerBy(float delta)
{
_timeLeft = Mathf.Clamp(_timeLeft + delta, 0f, maxTimePerQuestion);
}
/// <summary>Ratio 0–1 of time remaining (useful for timer bars).</summary>
public float GetCurrentTimeRatio()
{
if (maxTimePerQuestion <= 0f) return 0f;
return _timeLeft / maxTimePerQuestion;
}
// ─────────────────────────────────────────────────────────────────────
// Shared StartGame / loading skeleton
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// Starts the full loading + gameplay flow.
/// Call this from your UI "Start" button.
/// </summary>
public virtual void StartGame()
{
StartCoroutine(StartGameRoutine());
}
public virtual void ResetGame()
{
ResetBaseState();
}
/// <summary>
/// Shared loading routine: show loader → fetch → handle error → begin.
/// Override OnBeforeBeginGameplay() to insert game-specific steps
/// (e.g. countdown animation) between loading and BeginGameplay().
/// </summary>
protected virtual IEnumerator StartGameRoutine()
{
OnShowLoading();
string error = null;
yield return FetchQuestions(err => error = err);
if (!HasValidQuestions() || error != null)
{
OnShowError(error ?? "لا توجد أسئلة");
yield break;
}
OnHideLoading();
yield return OnBeforeBeginGameplay();
gameStartTime = DateTime.Now;
_timeLeft = maxTimePerQuestion;
_timerRunning = true;
BeginGameplay();
}
/// <summary>Override to call uiManager.ShowLoading(...).</summary>
protected virtual void OnShowLoading() { }
/// <summary>Override to call uiManager.HideLoading().</summary>
protected virtual void OnHideLoading() { }
/// <summary>Override to call uiManager.ShowError(...).</summary>
protected virtual void OnShowError(string message) { }
/// <summary>
/// Hook between loading and BeginGameplay.
/// Use it for countdowns, intro animations, etc.
/// Base implementation does nothing.
/// </summary>
protected virtual IEnumerator OnBeforeBeginGameplay() { yield break; }
// ─────────────────────────────────────────────────────────────────────
// Shared score helpers
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// Standard streak-based score calculation used by TF and MCQ:
/// base 100 pts + 25 per streak step above the threshold.
/// </summary>
protected int CalculateStreakScore(int streakThreshold = 2)
{
return 100 + (_streak > streakThreshold ? (_streak - streakThreshold) * 25 : 0);
}
// ─────────────────────────────────────────────────────────────────────
// Game history
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// Records the completed session to GameHistoryService.
/// Call at the end of VictorySequence / GameOverSequence.
/// </summary>
protected void RecordGameHistory()
{
// GameHistoryService.Instance.AddGame(GameTypeKey, _score, gameStartTime, DateTime.Now);
}
// ─────────────────────────────────────────────────────────────────────
// Shared reset helpers
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// Resets all shared counters and timer state.
/// Call at the start of ResetGame() in your subclass.
/// </summary>
protected void ResetBaseState()
{
StopAllCoroutines();
_score = _streak = _correctCount = _wrongCount = _currentIndex = _totalAsked = 0;
_timeLeft = 0f;
_timerRunning = false;
}
// ─────────────────────────────────────────────────────────────────────
// Utility
// ─────────────────────────────────────────────────────────────────────
/// <summary>Runs an action after a delay without blocking a coroutine.</summary>
protected IEnumerator DelayedAction(float delay, Action action)
{
yield return new WaitForSeconds(delay);
action?.Invoke();
}
// ─────────────────────────────────────────────────────────────────────
// Public read-only accessors (common across all games)
// ─────────────────────────────────────────────────────────────────────
public int Score => _score;
public int Streak => _streak;
public int CorrectCount => _correctCount;
public int WrongCount => _wrongCount;
public int CurrentQuestionIndex => _currentIndex;
// ─── Challenge mode ───────────────────────────────────────────────────
private ChallengeManager _challengeManager;
private bool _challengeChecked;
public event Action<bool, float, float> OnGameCompleted;
public bool IsChallengeMode
{
get
{
if (!_challengeChecked)
{
_challengeManager = FindFirstObjectByType<ChallengeManager>();
_challengeChecked = true;
}
return _challengeManager != null;
}
}
protected IEnumerator VictorySequence()
{
yield return SharedVictorySequence();
// In challenge mode, the ChallengeManager will handle moving to the next game or ending the challenge
if (IsChallengeMode)
OnGameCompleted?.Invoke(true, _timeLeft, _score);
else
yield return NoChallengeVictorySequence();
}
protected IEnumerator LoseSequence()
{
yield return SharedLoseSequence();
// In challenge mode, the ChallengeManager will handle showing results
if (IsChallengeMode)
OnGameCompleted?.Invoke(false, _timeLeft, _score);
else
yield return NoChallengeLoseSequence();
}
protected virtual IEnumerator SharedVictorySequence() { yield break; }
protected virtual IEnumerator SharedLoseSequence() { yield break; }
protected virtual IEnumerator NoChallengeVictorySequence() { yield break; }
protected virtual IEnumerator NoChallengeLoseSequence() { yield break; }
}
}
fileFormatVersion: 2 fileFormatVersion: 2
guid: c7f3fdf6e62442c5485a9ff394896b54 guid: 42fbf78681728ed4d92e5b86342792b3
\ No newline at end of file \ No newline at end of file
...@@ -6,13 +6,12 @@ using DG.Tweening; ...@@ -6,13 +6,12 @@ using DG.Tweening;
namespace com.al_arcade.tf namespace com.al_arcade.tf
{ {
using Microsoft.IdentityModel.Tokens;
using shared; using shared;
public enum TfGameState public enum TfGameState
{ Idle, Loading, Playing, Feedback, Complete, GameOver } { Idle, Loading, Playing, Feedback, Complete, GameOver }
public class TfGameManager : MonoBehaviour public class TfGameManager : BaseGameManager
{ {
public static TfGameManager Instance { get; private set; } public static TfGameManager Instance { get; private set; }
...@@ -20,6 +19,7 @@ namespace com.al_arcade.tf ...@@ -20,6 +19,7 @@ namespace com.al_arcade.tf
[SerializeField] private int stepsToWin = 5; [SerializeField] private int stepsToWin = 5;
[SerializeField] private float feedbackDuration = 1.2f; [SerializeField] private float feedbackDuration = 1.2f;
[SerializeField] private float stepDistance = 3f; [SerializeField] private float stepDistance = 3f;
[SerializeField] private float feedbackTime = 5f;
[Header("References")] [Header("References")]
public TfHandController handController; public TfHandController handController;
...@@ -29,108 +29,67 @@ namespace com.al_arcade.tf ...@@ -29,108 +29,67 @@ namespace com.al_arcade.tf
private TfGameState _state = TfGameState.Idle; private TfGameState _state = TfGameState.Idle;
private TfQuestion[] _questions; private TfQuestion[] _questions;
private int _currentIndex, _progress, _score, _streak; private int _progress;
private int _correctCount, _wrongCount, _totalAsked;
private bool _waitingForAnswer; private bool _waitingForAnswer;
private int _pendingAnswer = -1; private int _pendingAnswer = -1;
private DateTime gameStartTime; [Header("TF Events")]
[Header("Events")]
public UnityEvent onGameStart;
public UnityEvent<bool> onAnswerGiven;
public UnityEvent<int> onProgressChanged; public UnityEvent<int> onProgressChanged;
public UnityEvent onGameComplete; public UnityEvent onGameComplete;
public UnityEvent onGameOver; public UnityEvent onGameOver;
// --- ADDED: Timer Settings --- // ─── Singleton ────────────────────────────────────────────────────────
[SerializeField] private float maxTimePerQuestion = 30f; protected override void Awake()
[SerializeField] private float feedbackTime = 5;
private float _currentTime;
private bool _timerRunning;
public float GetCurrentTimeRatio()
{
if (maxTimePerQuestion <= 0f)
return 0f;
return _currentTime / maxTimePerQuestion;
}
private void Awake()
{ {
if (Instance != null && Instance != this) { Destroy(gameObject); return; } if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this; Instance = this;
base.Awake();
} }
public void StartGame() // ─── BaseGameManager implementation ──────────────────────────────────
{ protected override string GameTypeKey => "tf";
StartCoroutine(StartGameRoutine());
_currentTime = maxTimePerQuestion;
_timerRunning = true;
}
public void StartWithQuestions(TfQuestion[] questions) protected override IEnumerator FetchQuestions(Action<string> onError)
{ _questions = questions; BeginGameplay(); }
public void SubmitAnswer(bool answer)
{ {
if (!_waitingForAnswer) return;
_waitingForAnswer = false;
_pendingAnswer = answer ? 1 : 0;
}
public void ResetGame()
{
StopAllCoroutines();
_state = TfGameState.Idle;
_currentIndex = _progress = _score = _streak = 0;
_correctCount = _wrongCount = _totalAsked = 0;
_questions = null;
_waitingForAnswer = false;
_pendingAnswer = -1;
_timerRunning = false;
_currentTime = 0f;
if (productionLine != null) productionLine.ResetLine();
if (questionScreen != null) questionScreen.Clear();
if (handController != null) handController.Reset();
if (uiManager != null) uiManager.ResetUI();
}
private IEnumerator StartGameRoutine()
{
_state = TfGameState.Loading;
if (uiManager != null) uiManager.ShowLoading("جاري تحميل الأسئلة...");
var session = SSGameSession.EnsureInstance(); var session = SSGameSession.EnsureInstance();
var api = SSApiManager.EnsureInstance(); var api = SSApiManager.EnsureInstance();
string error = null;
yield return api.FetchTf( yield return api.FetchTf(
session.buildType, session.classCode, session.buildType, session.classCode,
session.questionCount, session.gradeId, session.questionCount, session.gradeId,
qs => _questions = qs, qs => _questions = qs,
err => error = err err => onError(err)
); );
}
protected override bool HasValidQuestions() =>
_questions != null && _questions.Length > 0;
if (error != null || _questions == null || _questions.Length == 0) protected override void OnShowLoading() =>
uiManager?.ShowLoading("جاري تحميل الأسئلة...");
protected override void OnHideLoading() =>
uiManager?.HideLoading();
protected override void OnShowError(string message)
{ {
if (uiManager != null) uiManager.ShowError(error ?? "لا توجد أسئلة"); uiManager?.ShowError(message);
_state = TfGameState.Idle; _state = TfGameState.Idle;
yield break;
} }
if (uiManager != null) uiManager.HideLoading(); protected override void OnTimerTick(float timeLeft) =>
uiManager?.SetTimer(timeLeft);
gameStartTime = DateTime.Now; protected override IEnumerator OnTimeUp()
BeginGameplay(); {
yield return HandleTimeUp();
} }
private void BeginGameplay() protected override void BeginGameplay()
{ {
_currentIndex = _progress = _score = _streak = 0; _progress = 0;
_correctCount = _wrongCount = _totalAsked = 0; // Reset base counters
_currentIndex = _score = _streak = _correctCount = _wrongCount = _totalAsked = 0;
_state = TfGameState.Playing; _state = TfGameState.Playing;
if (uiManager != null) if (uiManager != null)
...@@ -144,40 +103,40 @@ namespace com.al_arcade.tf ...@@ -144,40 +103,40 @@ namespace com.al_arcade.tf
StartCoroutine(GameLoop()); StartCoroutine(GameLoop());
} }
private void Update() // ─── Public API ───────────────────────────────────────────────────────
public void StartWithQuestions(TfQuestion[] questions)
{ {
if (!_timerRunning) return; _questions = questions;
BeginGameplay();
_currentTime -= Time.deltaTime; }
if (uiManager != null)
uiManager.SetTimer(_currentTime);
if (_currentTime <= 0f) public void SubmitAnswer(bool answer)
{ {
_currentTime = 0f; if (!_waitingForAnswer) return;
_timerRunning = false; _waitingForAnswer = false;
_pendingAnswer = answer ? 1 : 0;
StartCoroutine(HandleTimeUp());
}
} }
private IEnumerator HandleTimeUp()
{
Debug.Log("Handle Time UP");
_state = TfGameState.GameOver;
_timerRunning = false;
if (productionLine != null && productionLine.machineVFXController != null) public void ResetGame()
productionLine.machineVFXController.PlayExplosion(); {
ResetBaseState(); // clears shared counters + stops coroutines
_state = TfGameState.Idle;
_progress = 0;
_questions = null;
_waitingForAnswer = false;
_pendingAnswer = -1;
yield return new WaitForSeconds(1f); if (productionLine != null) productionLine.ResetLine();
if (questionScreen != null) questionScreen.Clear();
if (handController != null) handController.Reset();
if (uiManager != null) uiManager.ResetUI();
}
if (uiManager != null) // ─── Timer feedback override ──────────────────────────────────────────
uiManager.ShowResults(_score, _correctCount, _wrongCount, false); // TF adjusts time on correct/wrong, so it needs direct access
public float GetTimeLeft() => _timeLeft;
onGameOver?.Invoke(); // ─── Game loop ────────────────────────────────────────────────────────
}
private IEnumerator GameLoop() private IEnumerator GameLoop()
{ {
while (_progress < stepsToWin && _currentIndex < _questions.Length) while (_progress < stepsToWin && _currentIndex < _questions.Length)
...@@ -189,13 +148,11 @@ namespace com.al_arcade.tf ...@@ -189,13 +148,11 @@ namespace com.al_arcade.tf
_waitingForAnswer = true; _waitingForAnswer = true;
_pendingAnswer = -1; _pendingAnswer = -1;
_state = TfGameState.Playing; _state = TfGameState.Playing;
// --- ADDED: Start timer for this question ---
if (handController != null) handController.SetReady(true); if (handController != null) handController.SetReady(true);
while (_pendingAnswer < 0) while (_pendingAnswer < 0)
{ {
if (Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A)) if (Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A))
SubmitAnswer(true); SubmitAnswer(true);
if (Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D)) if (Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D))
...@@ -215,7 +172,7 @@ namespace com.al_arcade.tf ...@@ -215,7 +172,7 @@ namespace com.al_arcade.tf
_correctCount++; _correctCount++;
_streak++; _streak++;
_progress++; _progress++;
_score += 100 + (_streak > 2 ? (_streak - 2) * 25 : 0); _score += CalculateStreakScore(); // shared helper
if (handController != null) handController.PlayCorrectFeedback(playerSaidTrue); if (handController != null) handController.PlayCorrectFeedback(playerSaidTrue);
if (productionLine != null) yield return productionLine.MoveForward(stepDistance); if (productionLine != null) yield return productionLine.MoveForward(stepDistance);
...@@ -226,10 +183,11 @@ namespace com.al_arcade.tf ...@@ -226,10 +183,11 @@ namespace com.al_arcade.tf
{ if (audio.sfxCorrect != null) audio.PlayCorrect(); else audio.PlayCorrectBeep(); } { if (audio.sfxCorrect != null) audio.PlayCorrect(); else audio.PlayCorrectBeep(); }
var particles = SSParticleManager.Instance; var particles = SSParticleManager.Instance;
if (particles != null && Camera.main != null) if (particles != null)
particles.PlayCorrectBurst(new Vector3(-2, 6f, 13)); particles.PlayCorrectBurst(new Vector3(-2, 6f, 13));
_currentTime += 4;
uiManager?.UpdateTimer(_currentTime, true); UpdateTimerBy(4f); // shared helper
uiManager?.UpdateTimer(_timeLeft, true);
} }
else else
{ {
...@@ -251,12 +209,14 @@ namespace com.al_arcade.tf ...@@ -251,12 +209,14 @@ namespace com.al_arcade.tf
Camera.main.transform.DOShakePosition(0.4f, 0.3f, 15) Camera.main.transform.DOShakePosition(0.4f, 0.3f, 15)
.SetEase(Ease.OutQuad).SetId("camShake"); .SetEase(Ease.OutQuad).SetId("camShake");
} }
_currentTime -= 2;
uiManager?.UpdateTimer(_currentTime, false); UpdateTimerBy(-2f); // shared helper
uiManager?.UpdateTimer(_timeLeft, false);
} }
onAnswerGiven?.Invoke(isCorrect); onAnswerGiven?.Invoke(isCorrect);
onProgressChanged?.Invoke(_progress); onProgressChanged?.Invoke(_progress);
if (uiManager != null) if (uiManager != null)
{ {
uiManager.SetProgress(_progress, stepsToWin); uiManager.SetProgress(_progress, stepsToWin);
...@@ -276,11 +236,31 @@ namespace com.al_arcade.tf ...@@ -276,11 +236,31 @@ namespace com.al_arcade.tf
} }
} }
private IEnumerator VictorySequence() // ─── End sequences ────────────────────────────────────────────────────
private IEnumerator HandleTimeUp()
{
_state = TfGameState.GameOver;
yield return LoseSequence();
}
protected override IEnumerator SharedLoseSequence()
{
if (productionLine?.machineVFXController != null)
productionLine.machineVFXController.PlayExplosion();
yield return null;
}
protected override IEnumerator NoChallengeLoseSequence()
{ {
Debug.Log("victorySequance"); yield return new WaitForSeconds(1f);
uiManager?.ShowResults(_score, _correctCount, _wrongCount, false);
onGameOver?.Invoke();
}
protected override IEnumerator SharedVictorySequence()
{
_timerRunning = false; _timerRunning = false;
var audio = SSAudioManager.Instance; var audio = SSAudioManager.Instance;
if (audio != null) if (audio != null)
{ if (audio.sfxVictory != null) audio.PlayVictory(); else audio.PlaySuccessJingle(); } { if (audio.sfxVictory != null) audio.PlayVictory(); else audio.PlaySuccessJingle(); }
...@@ -290,17 +270,18 @@ namespace com.al_arcade.tf ...@@ -290,17 +270,18 @@ namespace com.al_arcade.tf
if (productionLine != null) yield return productionLine.ProductComplete(); if (productionLine != null) yield return productionLine.ProductComplete();
productionLine.machineVFXController.StopAllVFX(); productionLine.machineVFXController.StopAllVFX();
yield return new WaitForSeconds(1.5f); }
if (uiManager != null)
uiManager.ShowResults(_score, _correctCount, _wrongCount, true);
GameHistoryService.Instance.AddGame("tf", _score, gameStartTime, DateTime.Now); protected override IEnumerator NoChallengeVictorySequence()
{
yield return new WaitForSeconds(1.5f);
uiManager?.ShowResults(_score, _correctCount, _wrongCount, true);
onGameComplete?.Invoke();
} }
// ─── Extra public accessors ───────────────────────────────────────────
public int Progress => _progress; public int Progress => _progress;
public int StepsToWin => stepsToWin; public int StepsToWin => stepsToWin;
public int Score => _score;
} }
} }
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Correct The Sentence</title>
<!-- ═══ ANTI-CACHE ═══ -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- ═══ PRECONNECT TO CDN — speeds up HLS chunk fetching ═══ -->
<!-- Replace with your actual PeerTube domain -->
<link rel="preconnect" href="https://your-peertube-instance.com" crossorigin>
<link rel="dns-prefetch" href="https://your-peertube-instance.com">
<!-- ═══ PRELOAD HLS.js — starts downloading before Unity even loads ═══ -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/hls.js@1.5.17/dist/hls.min.js" as="script" crossorigin>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
/* Prevent pull-to-refresh on mobile */
overscroll-behavior: none;
touch-action: none;
}
#unity-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #000;
}
#unity-canvas {
background: #000;
/* Prevent blurry scaling */
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
/* ═══ LOADING SCREEN ═══ */
#loading-screen {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: #000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
transition: opacity 0.5s ease;
}
#loading-screen.fade-out {
opacity: 0;
pointer-events: none;
}
#loading-logo {
max-width: 200px;
max-height: 200px;
margin-bottom: 40px;
animation: logoPulse 2s ease-in-out infinite;
}
@keyframes logoPulse {
0%, 100% { transform: scale(1); opacity: 0.9; }
50% { transform: scale(1.05); opacity: 1; }
}
#loading-bar-container {
width: 280px;
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
overflow: hidden;
margin-bottom: 16px;
}
#loading-bar {
width: 0%;
height: 100%;
background: #FED700;
border-radius: 3px;
transition: width 0.3s ease;
box-shadow: 0 0 10px rgba(254, 215, 0, 0.4);
}
#loading-text {
color: rgba(255, 255, 255, 0.5);
font-size: 13px;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<div id="unity-container">
<canvas id="unity-canvas" tabindex="-1"></canvas>
</div>
<div id="loading-screen">
<img id="loading-logo" src="logo.png" alt="Loading">
<div id="loading-bar-container">
<div id="loading-bar"></div>
</div>
<div id="loading-text">Loading...</div>
</div>
<script>
// ═══ 16:9 ASPECT RATIO LOCK ═══
function resizeCanvas() {
var container = document.getElementById('unity-container');
var canvas = document.getElementById('unity-canvas');
var windowW = window.innerWidth;
var windowH = window.innerHeight;
var targetAspect = 16 / 9;
var windowAspect = windowW / windowH;
var canvasW, canvasH;
if (windowAspect > targetAspect) {
canvasH = windowH;
canvasW = Math.floor(windowH * targetAspect);
} else {
canvasW = windowW;
canvasH = Math.floor(windowW / targetAspect);
}
canvas.style.width = canvasW + 'px';
canvas.style.height = canvasH + 'px';
canvas.width = canvasW;
canvas.height = canvasH;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// ═══ PRELOAD HLS.js INTO CACHE BEFORE UNITY BOOTS ═══
// This way when the jslib calls ensureHls(), it's already loaded
(function() {
var hlsScript = document.createElement('script');
hlsScript.src = 'https://cdn.jsdelivr.net/npm/hls.js@1.5.17/dist/hls.min.js';
hlsScript.async = true;
document.head.appendChild(hlsScript);
})();
// ═══ UNITY LOADER ═══
var loadingBar = document.getElementById('loading-bar');
var loadingText = document.getElementById('loading-text');
var loadingScreen = document.getElementById('loading-screen');
// Cache-bust the loader URL in development
// Remove the timestamp parameter for production
var buildUrl = "Build";
var cacheBust = ""; // Set to "?t=" + Date.now() during development
var loaderUrl = buildUrl + "/CS Build.loader.js" + cacheBust;
var config = {
dataUrl: buildUrl + "/CS Build.data",
frameworkUrl: buildUrl + "/CS Build.framework.js",
codeUrl: buildUrl + "/CS Build.wasm",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "Correct The Sentence",
productVersion: "0.1.0",
// ═══ MEMORY SETTINGS ═══
// Match what you set in Player Settings
// These override if present
};
var script = document.createElement("script");
script.src = loaderUrl;
script.onload = function () {
createUnityInstance(
document.getElementById("unity-canvas"),
config,
function (progress) {
var pct = Math.round(progress * 100);
loadingBar.style.width = pct + '%';
loadingText.textContent = pct < 100 ? 'Loading... ' + pct + '%' : 'Starting...';
}
).then(function (instance) {
loadingScreen.classList.add('fade-out');
setTimeout(function () {
loadingScreen.style.display = 'none';
}, 600);
resizeCanvas();
// ═══ PREVENT ACCIDENTAL NAVIGATION ═══
window.addEventListener('beforeunload', function(e) {
e.preventDefault();
e.returnValue = '';
});
}).catch(function (message) {
loadingText.textContent = 'Error: ' + message;
loadingBar.style.background = '#ff3333';
console.error(message);
});
};
document.body.appendChild(script);
// ═══ PREVENT CONTEXT MENU ON CANVAS ═══
document.getElementById('unity-canvas').addEventListener('contextmenu', function(e) {
e.preventDefault();
});
// ═══ FOCUS CANVAS ON CLICK (fixes keyboard input) ═══
document.addEventListener('click', function() {
document.getElementById('unity-canvas').focus();
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Correct The Sentence</title>
<!-- ═══ ANTI-CACHE ═══ -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- ═══ PRECONNECT TO CDN — speeds up HLS chunk fetching ═══ -->
<!-- Replace with your actual PeerTube domain -->
<link rel="preconnect" href="https://your-peertube-instance.com" crossorigin>
<link rel="dns-prefetch" href="https://your-peertube-instance.com">
<!-- ═══ PRELOAD HLS.js — starts downloading before Unity even loads ═══ -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/hls.js@1.5.17/dist/hls.min.js" as="script" crossorigin>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
/* Prevent pull-to-refresh on mobile */
overscroll-behavior: none;
touch-action: none;
}
#unity-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #000;
}
#unity-canvas {
background: #000;
/* Prevent blurry scaling */
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
/* ═══ LOADING SCREEN ═══ */
#loading-screen {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: #000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
transition: opacity 0.5s ease;
}
#loading-screen.fade-out {
opacity: 0;
pointer-events: none;
}
#loading-logo {
max-width: 200px;
max-height: 200px;
margin-bottom: 40px;
animation: logoPulse 2s ease-in-out infinite;
}
@keyframes logoPulse {
0%, 100% { transform: scale(1); opacity: 0.9; }
50% { transform: scale(1.05); opacity: 1; }
}
#loading-bar-container {
width: 280px;
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
overflow: hidden;
margin-bottom: 16px;
}
#loading-bar {
width: 0%;
height: 100%;
background: #FED700;
border-radius: 3px;
transition: width 0.3s ease;
box-shadow: 0 0 10px rgba(254, 215, 0, 0.4);
}
#loading-text {
color: rgba(255, 255, 255, 0.5);
font-size: 13px;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<div id="unity-container">
<canvas id="unity-canvas" tabindex="-1"></canvas>
</div>
<div id="loading-screen">
<img id="loading-logo" src="logo.png" alt="Loading">
<div id="loading-bar-container">
<div id="loading-bar"></div>
</div>
<div id="loading-text">Loading...</div>
</div>
<script>
// ═══ 16:9 ASPECT RATIO LOCK ═══
function resizeCanvas() {
var container = document.getElementById('unity-container');
var canvas = document.getElementById('unity-canvas');
var windowW = window.innerWidth;
var windowH = window.innerHeight;
var targetAspect = 16 / 9;
var windowAspect = windowW / windowH;
var canvasW, canvasH;
if (windowAspect > targetAspect) {
canvasH = windowH;
canvasW = Math.floor(windowH * targetAspect);
} else {
canvasW = windowW;
canvasH = Math.floor(windowW / targetAspect);
}
canvas.style.width = canvasW + 'px';
canvas.style.height = canvasH + 'px';
canvas.width = canvasW;
canvas.height = canvasH;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// ═══ PRELOAD HLS.js INTO CACHE BEFORE UNITY BOOTS ═══
// This way when the jslib calls ensureHls(), it's already loaded
(function() {
var hlsScript = document.createElement('script');
hlsScript.src = 'https://cdn.jsdelivr.net/npm/hls.js@1.5.17/dist/hls.min.js';
hlsScript.async = true;
document.head.appendChild(hlsScript);
})();
// ═══ UNITY LOADER ═══
var loadingBar = document.getElementById('loading-bar');
var loadingText = document.getElementById('loading-text');
var loadingScreen = document.getElementById('loading-screen');
// Cache-bust the loader URL in development
// Remove the timestamp parameter for production
var buildUrl = "Build";
var cacheBust = ""; // Set to "?t=" + Date.now() during development
var loaderUrl = buildUrl + "/CS Build.loader.js" + cacheBust;
var config = {
dataUrl: buildUrl + "/CS Build.data",
frameworkUrl: buildUrl + "/CS Build.framework.js",
codeUrl: buildUrl + "/CS Build.wasm",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "Correct The Sentence",
productVersion: "0.1.0",
// ═══ MEMORY SETTINGS ═══
// Match what you set in Player Settings
// These override if present
};
var script = document.createElement("script");
script.src = loaderUrl;
script.onload = function () {
createUnityInstance(
document.getElementById("unity-canvas"),
config,
function (progress) {
var pct = Math.round(progress * 100);
loadingBar.style.width = pct + '%';
loadingText.textContent = pct < 100 ? 'Loading... ' + pct + '%' : 'Starting...';
}
).then(function (instance) {
loadingScreen.classList.add('fade-out');
setTimeout(function () {
loadingScreen.style.display = 'none';
}, 600);
resizeCanvas();
// ═══ PREVENT ACCIDENTAL NAVIGATION ═══
window.addEventListener('beforeunload', function(e) {
e.preventDefault();
e.returnValue = '';
});
}).catch(function (message) {
loadingText.textContent = 'Error: ' + message;
loadingBar.style.background = '#ff3333';
console.error(message);
});
};
document.body.appendChild(script);
// ═══ PREVENT CONTEXT MENU ON CANVAS ═══
document.getElementById('unity-canvas').addEventListener('contextmenu', function(e) {
e.preventDefault();
});
// ═══ FOCUS CANVAS ON CLICK (fixes keyboard input) ═══
document.addEventListener('click', function() {
document.getElementById('unity-canvas').focus();
});
</script>
</body>
</html>
...@@ -840,7 +840,7 @@ PlayerSettings: ...@@ -840,7 +840,7 @@ PlayerSettings:
webWasm2023: 0 webWasm2023: 0
webEnableSubmoduleStrippingCompatibility: 0 webEnableSubmoduleStrippingCompatibility: 0
scriptingDefineSymbols: scriptingDefineSymbols:
Android: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2 Android: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2;UNITEXT
EmbeddedLinux: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2 EmbeddedLinux: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
GameCoreScarlett: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2 GameCoreScarlett: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
GameCoreXboxOne: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2 GameCoreXboxOne: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
......
...@@ -33,13 +33,13 @@ EditorUserSettings: ...@@ -33,13 +33,13 @@ EditorUserSettings:
value: 5155075f06575f0a0f080e7a47270e444116497f782b7e367c7e4d6abbb9656a value: 5155075f06575f0a0f080e7a47270e444116497f782b7e367c7e4d6abbb9656a
flags: 0 flags: 0
RecentlyUsedSceneGuid-6: RecentlyUsedSceneGuid-6:
value: 0752035101010f0c54595b2046760e44134e4e7a7f7d71677c2c4836b7b4633e value: 5701055506000a030f5c542744260844404f4d73797975367c2c1e6ab7e2653d
flags: 0 flags: 0
RecentlyUsedSceneGuid-7: RecentlyUsedSceneGuid-7:
value: 5701055506000a030f5c542744260844404f4d73797975367c2c1e6ab7e2653d value: 52080c51560d5f03580b5e7242700c4446164f7d2e7f77612c281f32e0b8603d
flags: 0 flags: 0
RecentlyUsedSceneGuid-8: RecentlyUsedSceneGuid-8:
value: 52080c51560d5f03580b5e7242700c4446164f7d2e7f77612c281f32e0b8603d value: 0752035101010f0c54595b2046760e44134e4e7a7f7d71677c2c4836b7b4633e
flags: 0 flags: 0
RecentlyUsedSceneGuid-9: RecentlyUsedSceneGuid-9:
value: 060203560401505a595d0a7345200d44404e1b7e2d707e617b7f4d63e7b6606b value: 060203560401505a595d0a7345200d44404e1b7e2d707e617b7f4d63e7b6606b
......
...@@ -19,12 +19,12 @@ MonoBehaviour: ...@@ -19,12 +19,12 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 36 y: 36
width: 1918 width: 1920
height: 932 height: 941
m_MinSize: {x: 300, y: 112} m_MinSize: {x: 300, y: 112}
m_MaxSize: {x: 24288, y: 16192} m_MaxSize: {x: 24288, y: 16192}
vertical: 0 vertical: 0
controlID: 2275 controlID: 39
draggingID: 0 draggingID: 0
--- !u!114 &2 --- !u!114 &2
MonoBehaviour: MonoBehaviour:
...@@ -47,10 +47,10 @@ MonoBehaviour: ...@@ -47,10 +47,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Game\u200B" m_TextWithWhitespace: "Game\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 357 x: 356
y: 87 y: 79
width: 1105 width: 1107
height: 573 height: 579
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
...@@ -78,8 +78,8 @@ MonoBehaviour: ...@@ -78,8 +78,8 @@ MonoBehaviour:
m_UseMipMap: 0 m_UseMipMap: 0
m_VSyncEnabled: 0 m_VSyncEnabled: 0
m_Gizmos: 0 m_Gizmos: 0
m_Stats: 1 m_Stats: 0
m_SelectedSizes: 07000000000000000000000000000000000000000000000000000000000000000000000000000000 m_SelectedSizes: 07000000000000000000000012000000000000000000000000000000000000000000000000000000
m_ZoomArea: m_ZoomArea:
m_HRangeLocked: 0 m_HRangeLocked: 0
m_VRangeLocked: 0 m_VRangeLocked: 0
...@@ -106,26 +106,26 @@ MonoBehaviour: ...@@ -106,26 +106,26 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 21 y: 21
width: 1105 width: 1107
height: 552 height: 558
m_Scale: {x: 0.23, y: 0.23} m_Scale: {x: 0.23249999, y: 0.2325}
m_Translation: {x: 552.5, y: 276} m_Translation: {x: 553.5, y: 279}
m_MarginLeft: 0 m_MarginLeft: 0
m_MarginRight: 0 m_MarginRight: 0
m_MarginTop: 0 m_MarginTop: 0
m_MarginBottom: 0 m_MarginBottom: 0
m_LastShownAreaInsideMargins: m_LastShownAreaInsideMargins:
serializedVersion: 2 serializedVersion: 2
x: -2402.1738 x: -2380.6453
y: -1200 y: -1200
width: 4804.3477 width: 4761.2905
height: 2400 height: 2400
m_MinimalGUI: 1 m_MinimalGUI: 1
m_defaultScale: 0.23 m_defaultScale: 0.2325
m_LastWindowPixelSize: {x: 1105, y: 573} m_LastWindowPixelSize: {x: 1107, y: 579}
m_ClearInEditMode: 1 m_ClearInEditMode: 1
m_NoCameraWarning: 1 m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000000000000000000 m_LowResolutionForAspectRatios: 01000001000000000000
m_XRRenderMode: 0 m_XRRenderMode: 0
m_RenderTexture: {fileID: 0} m_RenderTexture: {fileID: 0}
m_showToolbar: 1 m_showToolbar: 1
...@@ -148,12 +148,12 @@ MonoBehaviour: ...@@ -148,12 +148,12 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 1463 width: 1465
height: 932 height: 941
m_MinSize: {x: 200, y: 112} m_MinSize: {x: 200, y: 112}
m_MaxSize: {x: 16192, y: 16192} m_MaxSize: {x: 16192, y: 16192}
vertical: 1 vertical: 1
controlID: 2276 controlID: 40
draggingID: 0 draggingID: 0
--- !u!114 &4 --- !u!114 &4
MonoBehaviour: MonoBehaviour:
...@@ -174,12 +174,12 @@ MonoBehaviour: ...@@ -174,12 +174,12 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 1463 width: 1465
height: 599 height: 605
m_MinSize: {x: 200, y: 56} m_MinSize: {x: 200, y: 56}
m_MaxSize: {x: 16192, y: 8096} m_MaxSize: {x: 16192, y: 8096}
vertical: 0 vertical: 0
controlID: 2277 controlID: 41
draggingID: 0 draggingID: 0
--- !u!114 &5 --- !u!114 &5
MonoBehaviour: MonoBehaviour:
...@@ -199,7 +199,7 @@ MonoBehaviour: ...@@ -199,7 +199,7 @@ MonoBehaviour:
x: 0 x: 0
y: 0 y: 0
width: 356 width: 356
height: 599 height: 605
m_MinSize: {x: 201, y: 226} m_MinSize: {x: 201, y: 226}
m_MaxSize: {x: 4001, y: 4026} m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 6} m_ActualView: {fileID: 6}
...@@ -228,10 +228,10 @@ MonoBehaviour: ...@@ -228,10 +228,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Hierarchy\u200B" m_TextWithWhitespace: "Hierarchy\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 1 x: 0
y: 87 y: 79
width: 355 width: 355
height: 573 height: 579
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
...@@ -248,17 +248,11 @@ MonoBehaviour: ...@@ -248,17 +248,11 @@ MonoBehaviour:
m_SceneHierarchy: m_SceneHierarchy:
m_TreeViewState: m_TreeViewState:
scrollPos: {x: 0, y: 0} scrollPos: {x: 0, y: 0}
m_SelectedIDs: m_SelectedIDs: []
- m_Data: 56538
m_LastClickedID: m_LastClickedID:
m_Data: 56538 m_Data: 0
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: -1346 - m_Data: -1344
- m_Data: 56482
- m_Data: 56500
- m_Data: 56528
- m_Data: 56538
- m_Data: 56548
m_RenameOverlay: m_RenameOverlay:
m_UserAcceptedRename: 0 m_UserAcceptedRename: 0
m_Name: m_Name:
...@@ -301,8 +295,8 @@ MonoBehaviour: ...@@ -301,8 +295,8 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 356 x: 356
y: 0 y: 0
width: 1107 width: 1109
height: 599 height: 605
m_MinSize: {x: 202, y: 226} m_MinSize: {x: 202, y: 226}
m_MaxSize: {x: 4002, y: 4026} m_MaxSize: {x: 4002, y: 4026}
m_ActualView: {fileID: 2} m_ActualView: {fileID: 2}
...@@ -1024,9 +1018,9 @@ MonoBehaviour: ...@@ -1024,9 +1018,9 @@ MonoBehaviour:
m_Position: m_Position:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 599 y: 605
width: 1463 width: 1465
height: 333 height: 336
m_MinSize: {x: 101, y: 126} m_MinSize: {x: 101, y: 126}
m_MaxSize: {x: 4001, y: 4026} m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 11} m_ActualView: {fileID: 11}
...@@ -1087,7 +1081,7 @@ MonoBehaviour: ...@@ -1087,7 +1081,7 @@ MonoBehaviour:
m_SkipHidden: 0 m_SkipHidden: 0
m_SearchArea: 1 m_SearchArea: 1
m_Folders: m_Folders:
- Assets/App/Core - Assets/AppUI/Scenes
m_Globs: [] m_Globs: []
m_ProductIds: m_ProductIds:
m_AnyWithAssetOrigin: 0 m_AnyWithAssetOrigin: 0
...@@ -1097,7 +1091,7 @@ MonoBehaviour: ...@@ -1097,7 +1091,7 @@ MonoBehaviour:
m_ViewMode: 1 m_ViewMode: 1
m_StartGridSize: 96 m_StartGridSize: 96
m_LastFolders: m_LastFolders:
- Assets/App/Core - Assets/AppUI/Scenes
m_LastFoldersGridSize: 96 m_LastFoldersGridSize: 96
m_LastProjectPath: D:\Dev\Projects\SSBookMinigames\My project m_LastProjectPath: D:\Dev\Projects\SSBookMinigames\My project
m_LockTracker: m_LockTracker:
...@@ -1106,14 +1100,12 @@ MonoBehaviour: ...@@ -1106,14 +1100,12 @@ MonoBehaviour:
m_FolderTreeState: m_FolderTreeState:
scrollPos: {x: 0, y: 79} scrollPos: {x: 0, y: 79}
m_SelectedIDs: m_SelectedIDs:
- m_Data: 57168 - m_Data: 74206
m_LastClickedID: m_LastClickedID:
m_Data: 57168 m_Data: 74206
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: 0 - m_Data: 0
- m_Data: 56686 - m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay: m_RenameOverlay:
m_UserAcceptedRename: 0 m_UserAcceptedRename: 0
m_Name: m_Name:
...@@ -1147,8 +1139,6 @@ MonoBehaviour: ...@@ -1147,8 +1139,6 @@ MonoBehaviour:
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: 0 - m_Data: 0
- m_Data: 56686 - m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay: m_RenameOverlay:
m_UserAcceptedRename: 0 m_UserAcceptedRename: 0
m_Name: m_Name:
...@@ -1176,8 +1166,8 @@ MonoBehaviour: ...@@ -1176,8 +1166,8 @@ MonoBehaviour:
m_ResourceFile: m_ResourceFile:
m_ListAreaState: m_ListAreaState:
m_SelectedInstanceIDs: m_SelectedInstanceIDs:
- m_Data: 56474 - m_Data: -165430
m_LastClickedInstanceID: 56474 m_LastClickedInstanceID: -165430
m_HadKeyboardFocusLastEvent: 0 m_HadKeyboardFocusLastEvent: 0
m_ExpandedInstanceIDs: m_ExpandedInstanceIDs:
- m_Data: 46526 - m_Data: 46526
...@@ -1233,10 +1223,10 @@ MonoBehaviour: ...@@ -1233,10 +1223,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Console\u200B" m_TextWithWhitespace: "Console\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 1 x: 0
y: 686 y: 684
width: 1462 width: 1464
height: 307 height: 310
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
...@@ -1290,7 +1280,7 @@ MonoBehaviour: ...@@ -1290,7 +1280,7 @@ MonoBehaviour:
m_DynamicPanelBehavior: 0 m_DynamicPanelBehavior: 0
m_LockTracker: m_LockTracker:
m_IsLocked: 0 m_IsLocked: 0
m_LastSelectedObjectID: 56538 m_LastSelectedObjectID: 0
--- !u!114 &13 --- !u!114 &13
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 52 m_ObjectHideFlags: 52
...@@ -1420,8 +1410,6 @@ MonoBehaviour: ...@@ -1420,8 +1410,6 @@ MonoBehaviour:
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: 0 - m_Data: 0
- m_Data: 56686 - m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay: m_RenameOverlay:
m_UserAcceptedRename: 0 m_UserAcceptedRename: 0
m_Name: m_Name:
...@@ -1455,8 +1443,6 @@ MonoBehaviour: ...@@ -1455,8 +1443,6 @@ MonoBehaviour:
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: 0 - m_Data: 0
- m_Data: 56686 - m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay: m_RenameOverlay:
m_UserAcceptedRename: 0 m_UserAcceptedRename: 0
m_Name: m_Name:
...@@ -1531,10 +1517,10 @@ MonoBehaviour: ...@@ -1531,10 +1517,10 @@ MonoBehaviour:
m_Children: [] m_Children: []
m_Position: m_Position:
serializedVersion: 2 serializedVersion: 2
x: 1463 x: 1465
y: 0 y: 0
width: 455 width: 455
height: 932 height: 941
m_MinSize: {x: 276, y: 76} m_MinSize: {x: 276, y: 76}
m_MaxSize: {x: 4001, y: 4026} m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 16} m_ActualView: {fileID: 16}
...@@ -1563,10 +1549,10 @@ MonoBehaviour: ...@@ -1563,10 +1549,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Inspector\u200B" m_TextWithWhitespace: "Inspector\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 1464 x: 1465
y: 87 y: 79
width: 454 width: 454
height: 906 height: 915
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
......
...@@ -14,16 +14,16 @@ MonoBehaviour: ...@@ -14,16 +14,16 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.ContainerWindow m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.ContainerWindow
m_PixelRect: m_PixelRect:
serializedVersion: 2 serializedVersion: 2
x: 1 x: 0
y: 51 y: 43
width: 1918 width: 1920
height: 988 height: 997
m_ShowMode: 4 m_ShowMode: 4
m_Title: Project m_Title: Hierarchy
m_RootView: {fileID: 2} m_RootView: {fileID: 2}
m_MinSize: {x: 875, y: 300} m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000} m_MaxSize: {x: 10000, y: 10000}
m_Maximized: 0 m_Maximized: 1
--- !u!114 &2 --- !u!114 &2
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 52 m_ObjectHideFlags: 52
...@@ -44,8 +44,8 @@ MonoBehaviour: ...@@ -44,8 +44,8 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 1918 width: 1920
height: 988 height: 997
m_MinSize: {x: 875, y: 300} m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000} m_MaxSize: {x: 10000, y: 10000}
m_UseTopView: 1 m_UseTopView: 1
...@@ -69,7 +69,7 @@ MonoBehaviour: ...@@ -69,7 +69,7 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 1918 width: 1920
height: 36 height: 36
m_MinSize: {x: 50, y: 50} m_MinSize: {x: 50, y: 50}
m_MaxSize: {x: 4000, y: 4000} m_MaxSize: {x: 4000, y: 4000}
...@@ -90,8 +90,8 @@ MonoBehaviour: ...@@ -90,8 +90,8 @@ MonoBehaviour:
m_Position: m_Position:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 968 y: 977
width: 1918 width: 1920
height: 20 height: 20
m_MinSize: {x: 0, y: 0} m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0} m_MaxSize: {x: 0, y: 0}
...@@ -114,12 +114,12 @@ MonoBehaviour: ...@@ -114,12 +114,12 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 36 y: 36
width: 1918 width: 1920
height: 932 height: 941
m_MinSize: {x: 300, y: 112} m_MinSize: {x: 300, y: 112}
m_MaxSize: {x: 24288, y: 16192} m_MaxSize: {x: 24288, y: 16192}
vertical: 0 vertical: 0
controlID: 1755 controlID: 160
draggingID: 0 draggingID: 0
--- !u!114 &6 --- !u!114 &6
MonoBehaviour: MonoBehaviour:
...@@ -140,12 +140,12 @@ MonoBehaviour: ...@@ -140,12 +140,12 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 1463 width: 1465
height: 932 height: 941
m_MinSize: {x: 200, y: 112} m_MinSize: {x: 200, y: 112}
m_MaxSize: {x: 16192, y: 16192} m_MaxSize: {x: 16192, y: 16192}
vertical: 1 vertical: 1
controlID: 1756 controlID: 161
draggingID: 0 draggingID: 0
--- !u!114 &7 --- !u!114 &7
MonoBehaviour: MonoBehaviour:
...@@ -166,12 +166,12 @@ MonoBehaviour: ...@@ -166,12 +166,12 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 1463 width: 1465
height: 599 height: 605
m_MinSize: {x: 200, y: 56} m_MinSize: {x: 200, y: 56}
m_MaxSize: {x: 16192, y: 8096} m_MaxSize: {x: 16192, y: 8096}
vertical: 0 vertical: 0
controlID: 1757 controlID: 162
draggingID: 0 draggingID: 0
--- !u!114 &8 --- !u!114 &8
MonoBehaviour: MonoBehaviour:
...@@ -190,8 +190,8 @@ MonoBehaviour: ...@@ -190,8 +190,8 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 0
width: 356 width: 355
height: 599 height: 605
m_MinSize: {x: 201, y: 226} m_MinSize: {x: 201, y: 226}
m_MaxSize: {x: 4001, y: 4026} m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 14} m_ActualView: {fileID: 14}
...@@ -214,10 +214,10 @@ MonoBehaviour: ...@@ -214,10 +214,10 @@ MonoBehaviour:
m_Children: [] m_Children: []
m_Position: m_Position:
serializedVersion: 2 serializedVersion: 2
x: 356 x: 355
y: 0 y: 0
width: 1107 width: 1110
height: 599 height: 605
m_MinSize: {x: 202, y: 226} m_MinSize: {x: 202, y: 226}
m_MaxSize: {x: 4002, y: 4026} m_MaxSize: {x: 4002, y: 4026}
m_ActualView: {fileID: 13} m_ActualView: {fileID: 13}
...@@ -236,26 +236,26 @@ MonoBehaviour: ...@@ -236,26 +236,26 @@ MonoBehaviour:
m_Enabled: 1 m_Enabled: 1
m_EditorHideFlags: 1 m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: ProjectBrowser m_Name: ConsoleWindow
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_Children: [] m_Children: []
m_Position: m_Position:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 599 y: 605
width: 1463 width: 1465
height: 333 height: 336
m_MinSize: {x: 231, y: 276} m_MinSize: {x: 101, y: 126}
m_MaxSize: {x: 10001, y: 10026} m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 16} m_ActualView: {fileID: 17}
m_Panes: m_Panes:
- {fileID: 16} - {fileID: 16}
- {fileID: 17} - {fileID: 17}
- {fileID: 18} - {fileID: 18}
- {fileID: 19} - {fileID: 19}
- {fileID: 20} - {fileID: 20}
m_Selected: 0 m_Selected: 1
m_LastSelected: 1 m_LastSelected: 0
--- !u!114 &11 --- !u!114 &11
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 52 m_ObjectHideFlags: 52
...@@ -271,10 +271,10 @@ MonoBehaviour: ...@@ -271,10 +271,10 @@ MonoBehaviour:
m_Children: [] m_Children: []
m_Position: m_Position:
serializedVersion: 2 serializedVersion: 2
x: 1463 x: 1465
y: 0 y: 0
width: 455 width: 455
height: 932 height: 941
m_MinSize: {x: 276, y: 76} m_MinSize: {x: 276, y: 76}
m_MaxSize: {x: 4001, y: 4026} m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 21} m_ActualView: {fileID: 21}
...@@ -304,8 +304,8 @@ MonoBehaviour: ...@@ -304,8 +304,8 @@ MonoBehaviour:
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 0 y: 43
width: 1918 width: 1920
height: 36 height: 36
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
...@@ -583,10 +583,10 @@ MonoBehaviour: ...@@ -583,10 +583,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Game\u200B" m_TextWithWhitespace: "Game\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 357 x: 355
y: 24 y: 79
width: 1105 width: 1108
height: 573 height: 579
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
...@@ -614,8 +614,8 @@ MonoBehaviour: ...@@ -614,8 +614,8 @@ MonoBehaviour:
m_UseMipMap: 0 m_UseMipMap: 0
m_VSyncEnabled: 0 m_VSyncEnabled: 0
m_Gizmos: 0 m_Gizmos: 0
m_Stats: 1 m_Stats: 0
m_SelectedSizes: 07000000000000000000000000000000000000000000000000000000000000000000000000000000 m_SelectedSizes: 07000000000000000000000012000000000000000000000000000000000000000000000000000000
m_ZoomArea: m_ZoomArea:
m_HRangeLocked: 0 m_HRangeLocked: 0
m_VRangeLocked: 0 m_VRangeLocked: 0
...@@ -642,26 +642,26 @@ MonoBehaviour: ...@@ -642,26 +642,26 @@ MonoBehaviour:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 21 y: 21
width: 1105 width: 1108
height: 552 height: 558
m_Scale: {x: 0.23, y: 0.23} m_Scale: {x: 0.2325, y: 0.2325}
m_Translation: {x: 552.5, y: 276} m_Translation: {x: 554, y: 279}
m_MarginLeft: 0 m_MarginLeft: 0
m_MarginRight: 0 m_MarginRight: 0
m_MarginTop: 0 m_MarginTop: 0
m_MarginBottom: 0 m_MarginBottom: 0
m_LastShownAreaInsideMargins: m_LastShownAreaInsideMargins:
serializedVersion: 2 serializedVersion: 2
x: -2402.1738 x: -2382.7957
y: -1200 y: -1200
width: 4804.3477 width: 4765.5913
height: 2400 height: 2400
m_MinimalGUI: 1 m_MinimalGUI: 1
m_defaultScale: 0.23 m_defaultScale: 0.2325
m_LastWindowPixelSize: {x: 1105, y: 573} m_LastWindowPixelSize: {x: 1108, y: 579}
m_ClearInEditMode: 1 m_ClearInEditMode: 1
m_NoCameraWarning: 1 m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000000000000000000 m_LowResolutionForAspectRatios: 01000001000000000000
m_XRRenderMode: 0 m_XRRenderMode: 0
m_RenderTexture: {fileID: 0} m_RenderTexture: {fileID: 0}
m_showToolbar: 1 m_showToolbar: 1
...@@ -687,9 +687,9 @@ MonoBehaviour: ...@@ -687,9 +687,9 @@ MonoBehaviour:
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 0
y: 24 y: 79
width: 355 width: 354
height: 573 height: 579
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
...@@ -706,16 +706,14 @@ MonoBehaviour: ...@@ -706,16 +706,14 @@ MonoBehaviour:
m_SceneHierarchy: m_SceneHierarchy:
m_TreeViewState: m_TreeViewState:
scrollPos: {x: 0, y: 0} scrollPos: {x: 0, y: 0}
m_SelectedIDs: [] m_SelectedIDs:
- m_Data: -2616
m_LastClickedID: m_LastClickedID:
m_Data: 0 m_Data: -2616
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: -3222 - m_Data: -2614
- m_Data: -3132 - m_Data: -1344
- m_Data: -1346 - m_Data: -12
- m_Data: 56478
- m_Data: 56508
- m_Data: 56518
m_RenameOverlay: m_RenameOverlay:
m_UserAcceptedRename: 0 m_UserAcceptedRename: 0
m_Name: m_Name:
...@@ -1459,8 +1457,8 @@ MonoBehaviour: ...@@ -1459,8 +1457,8 @@ MonoBehaviour:
m_TextWithWhitespace: "Project\u200B" m_TextWithWhitespace: "Project\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 0 x: 1
y: 623 y: 686
width: 1462 width: 1462
height: 307 height: 307
m_SerializedDataModeController: m_SerializedDataModeController:
...@@ -1487,7 +1485,7 @@ MonoBehaviour: ...@@ -1487,7 +1485,7 @@ MonoBehaviour:
m_SkipHidden: 0 m_SkipHidden: 0
m_SearchArea: 1 m_SearchArea: 1
m_Folders: m_Folders:
- Assets/App/Core - Assets/AppUI/Scenes
m_Globs: [] m_Globs: []
m_ProductIds: m_ProductIds:
m_AnyWithAssetOrigin: 0 m_AnyWithAssetOrigin: 0
...@@ -1497,7 +1495,7 @@ MonoBehaviour: ...@@ -1497,7 +1495,7 @@ MonoBehaviour:
m_ViewMode: 1 m_ViewMode: 1
m_StartGridSize: 96 m_StartGridSize: 96
m_LastFolders: m_LastFolders:
- Assets/App/Core - Assets/AppUI/Scenes
m_LastFoldersGridSize: 96 m_LastFoldersGridSize: 96
m_LastProjectPath: D:\Dev\Projects\SSBookMinigames\My project m_LastProjectPath: D:\Dev\Projects\SSBookMinigames\My project
m_LockTracker: m_LockTracker:
...@@ -1506,9 +1504,9 @@ MonoBehaviour: ...@@ -1506,9 +1504,9 @@ MonoBehaviour:
m_FolderTreeState: m_FolderTreeState:
scrollPos: {x: 0, y: 79} scrollPos: {x: 0, y: 79}
m_SelectedIDs: m_SelectedIDs:
- m_Data: 60184 - m_Data: 74206
m_LastClickedID: m_LastClickedID:
m_Data: 60184 m_Data: 74206
m_ExpandedIDs: m_ExpandedIDs:
- m_Data: 0 - m_Data: 0
- m_Data: 56686 - m_Data: 56686
...@@ -1572,9 +1570,9 @@ MonoBehaviour: ...@@ -1572,9 +1570,9 @@ MonoBehaviour:
m_ResourceFile: m_ResourceFile:
m_ListAreaState: m_ListAreaState:
m_SelectedInstanceIDs: m_SelectedInstanceIDs:
- m_Data: 60216 - m_Data: -165430
m_LastClickedInstanceID: 60216 m_LastClickedInstanceID: -165430
m_HadKeyboardFocusLastEvent: 1 m_HadKeyboardFocusLastEvent: 0
m_ExpandedInstanceIDs: m_ExpandedInstanceIDs:
- m_Data: 46526 - m_Data: 46526
- m_Data: 61214 - m_Data: 61214
...@@ -1629,10 +1627,10 @@ MonoBehaviour: ...@@ -1629,10 +1627,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Console\u200B" m_TextWithWhitespace: "Console\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 1 x: 0
y: 686 y: 684
width: 1462 width: 1464
height: 307 height: 310
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
...@@ -1929,10 +1927,10 @@ MonoBehaviour: ...@@ -1929,10 +1927,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Inspector\u200B" m_TextWithWhitespace: "Inspector\u200B"
m_Pos: m_Pos:
serializedVersion: 2 serializedVersion: 2
x: 1464 x: 1465
y: 24 y: 79
width: 454 width: 454
height: 906 height: 915
m_SerializedDataModeController: m_SerializedDataModeController:
m_DataMode: 0 m_DataMode: 0
m_PreferredDataMode: 0 m_PreferredDataMode: 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment