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:
propertyPath: m_IsActive
value: 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}
propertyPath: backButton
value:
......
......@@ -211,8 +211,7 @@ Transform:
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1399870137}
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &603037699
......@@ -517,6 +516,7 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
<<<<<<< HEAD
--- !u!1 &1283619807
GameObject:
m_ObjectHideFlags: 0
......@@ -632,6 +632,8 @@ Transform:
m_CorrespondingSourceObject: {fileID: 6955428004947038011, guid: 81b128416c1c2356c8d645340d865e65, type: 3}
m_PrefabInstance: {fileID: 1399870136}
m_PrefabAsset: {fileID: 0}
=======
>>>>>>> origin/pt_refactor_managers
--- !u!1 &1878716305
GameObject:
m_ObjectHideFlags: 0
......
......@@ -23,29 +23,19 @@ namespace com.al_arcade.cs
Complete,
}
public class CsGameManager : MonoBehaviour, IChallengeGame
public class CsGameManager : BaseGameManager
{
public static CsGameManager Instance { get; private set; }
[Header("Settings")]
[SerializeField]
private float sentenceShowDelay = 0.5f;
[SerializeField]
private float feedbackDuration = 1.5f;
[SerializeField]
private float wordClickCooldown = 0.3f;
[SerializeField] private float sentenceShowDelay = 0.5f;
[SerializeField] private float feedbackDuration = 1.5f;
[SerializeField] private float wordClickCooldown = 0.3f;
[Header("Arc Settings")]
[SerializeField]
private float arcRadius = 15f;
[SerializeField]
private float arcSpanDegrees = 120f;
[SerializeField]
private float wordGap = 0.2f;
[SerializeField] private float arcRadius = 15f;
[SerializeField] private float arcSpanDegrees = 120f;
[SerializeField] private float wordGap = 0.2f;
[Header("References")]
public CsBotController bot;
......@@ -54,158 +44,164 @@ namespace com.al_arcade.cs
private CsGameState _state = CsGameState.Idle;
private CsQuestion[] _questions;
private int _currentIndex;
private int _score,
_streak,
_correctCount,
_wrongCount,
_wrongClicks;
private int _wrongClicks;
private List<CsWordButton> _wordButtons = new();
private CsWordButton _wrongWordButton;
private bool _wordClickLocked;
[Header("Events")]
public UnityEvent onGameStart;
public UnityEvent<bool> onAnswerGiven;
[Header("CS Events")]
public UnityEvent<int> onGameComplete;
private Action<bool> _onOptionDropped;
private float _timeLeft;
private bool isTicking;
private DateTime gameStartTime;
// if it reaches 5, you win the game;
int _deltaChangeInSize;
bool showHint = true;
private bool _isTicking;
private int _deltaChangeInSize;
private bool _showHint = true;
CinemachineTargetGroup _targetGroup;
CinemachineGroupFraming _groupFraming;
public event Action<bool, float> OnGameCompleted;
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;
base.Awake();
_targetGroup = FindFirstObjectByType<CinemachineTargetGroup>();
_groupFraming = FindFirstObjectByType<CinemachineGroupFraming>();
}
private void Update()
// ─── BaseGameManager implementation ──────────────────────────────────
protected override string GameTypeKey => "cs";
protected override IEnumerator FetchQuestions(Action<string> onError)
{
if (_state == CsGameState.WaitingForWordClick || _state == CsGameState.WaitingForDrop)
{
_timeLeft -= Time.deltaTime;
if (_timeLeft <= 0)
{
_timeLeft = 0;
_state = CsGameState.Complete;
StartCoroutine(LoseSequence());
}
var session = SSGameSession.EnsureInstance();
var api = SSApiManager.EnsureInstance();
if (_timeLeft < 4f && !isTicking)
{
isTicking = true;
var audio = SSAudioManager.Instance;
audio.Tick(true);
}
else if (_timeLeft >= 4f && isTicking)
{
isTicking = false;
var audio = SSAudioManager.Instance;
audio.Tick(false);
}
if (uiManager != null && uiManager.isMusicOn)
SSAudioManager.EnsureInstance().PlayMusic();
uiManager.SetTimer(_timeLeft);
}
yield return api.FetchCs(
session.buildType, session.classCode,
session.questionCount, session.gradeId,
qs => _questions = qs,
err => onError(err)
);
}
protected override bool HasValidQuestions() =>
_questions != null && _questions.Length > 0;
protected override void OnShowLoading() =>
uiManager?.ShowLoading("جاري تحميل الأسئلة...");
protected override void OnHideLoading() =>
uiManager?.HideLoading();
void UpdateTimer(int delta)
protected override void OnShowError(string message)
{
_timeLeft += delta;
_timeLeft = Mathf.Clamp(_timeLeft, 0, CsPrefabBuilder.Instance.startTime);
uiManager?.ShowError(message);
_state = CsGameState.Idle;
}
protected override IEnumerator OnBeforeBeginGameplay()
{
if (uiManager != null)
yield return uiManager.ShowCountDown();
}
if (_timeLeft < 4f && !isTicking)
protected override void OnTimerTick(float timeLeft)
{
// Ticking sound when under 4 seconds
if (timeLeft < 4f && !_isTicking)
{
isTicking = true;
var audio = SSAudioManager.Instance;
audio.Tick(true);
_isTicking = true;
SSAudioManager.Instance?.Tick(true);
}
else if (_timeLeft >= 4f && isTicking)
else if (timeLeft >= 4f && _isTicking)
{
isTicking = false;
var audio = SSAudioManager.Instance;
audio.Tick(false);
_isTicking = false;
SSAudioManager.Instance?.Tick(false);
}
uiManager.UpdateTimer(_timeLeft, delta > 0);
uiManager?.SetTimer(timeLeft);
}
protected override IEnumerator OnTimeUp()
{
_state = CsGameState.Complete;
yield return LoseSequence();
}
protected override void BeginGameplay()
{
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
_deltaChangeInSize = 0;
_timeLeft = CsPrefabBuilder.Instance.startTime; // CS uses its own start time
_timerRunning = true;
if (uiManager != null)
{
uiManager.ShowGameUI();
uiManager.SetScore(0);
uiManager.SetProgress(0, _questions.Length);
}
onGameStart?.Invoke();
StartCoroutine(QuestionLoop());
}
public void StartGame() => StartCoroutine(StartGameRoutine());
// ─── 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 API ───────────────────────────────────────────────────────
public void StartWithQuestions(CsQuestion[] questions)
{
_questions = questions;
BeginGameplay();
}
public void ResetGame()
public override void ResetGame()
{
StopAllCoroutines();
ResetBaseState();
_deltaChangeInSize = 0;
_state = CsGameState.Idle;
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
_isTicking = false;
ClearWordButtons();
if (uiManager != null)
uiManager.ResetUI();
if (bot != null)
bot.ResetBot();
uiManager?.ResetUI();
bot?.ResetBot();
}
public void OnWordClicked(CsWordButton wordButton)
{
if (_state != CsGameState.WaitingForWordClick)
return;
if (_wordClickLocked)
return;
if (_state != CsGameState.WaitingForWordClick) return;
if (_wordClickLocked) return;
if (wordButton.IsWrong)
{
_wrongWordButton = wordButton;
wordButton.Highlight(true);
var audio = SSAudioManager.Instance;
if (audio != null)
audio.PlayCorrectChoice();
var particles = SSParticleManager.Instance;
if (particles != null)
particles.PlaySparks(wordButton.transform.position, SSColorPalette.Accent);
SSAudioManager.Instance?.PlayCorrectChoice();
SSParticleManager.Instance?.PlaySparks(wordButton.transform.position, SSColorPalette.Accent);
_state = CsGameState.WaitingForDrop;
ShowOptions();
}
else
{
UpdateTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
AdjustTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
_wrongClicks++;
_wordClickLocked = true;
wordButton.ShakeWrong();
var audio = SSAudioManager.Instance;
if (audio != null)
audio.PlayWrongBeep();
SSAudioManager.Instance?.PlayWrongBeep();
StartCoroutine(UnlockAfterCooldown());
}
}
......@@ -215,78 +211,34 @@ namespace com.al_arcade.cs
_onOptionDropped?.Invoke(isCorrectOption);
}
private IEnumerator StartGameRoutine()
{
_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()
// ─── Timer helpers ────────────────────────────────────────────────────
private void AdjustTimer(float delta)
{
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
_timeLeft = CsPrefabBuilder.Instance.startTime;
UpdateTimerBy(delta);
_timeLeft = Mathf.Clamp(_timeLeft, 0, CsPrefabBuilder.Instance.startTime);
if (uiManager != null)
{
uiManager.ShowGameUI();
uiManager.SetScore(0);
uiManager.SetProgress(0, _questions.Length);
}
// Re-check tick state after adjustment
if (_timeLeft < 4f && !_isTicking)
{ _isTicking = true; SSAudioManager.Instance?.Tick(true); }
else if (_timeLeft >= 4f && _isTicking)
{ _isTicking = false; SSAudioManager.Instance?.Tick(false); }
onGameStart?.Invoke();
StartCoroutine(QuestionLoop());
uiManager?.UpdateTimer(_timeLeft, delta > 0);
}
// ─── Game loop ────────────────────────────────────────────────────────
private IEnumerator QuestionLoop()
{
while (_currentIndex < _questions.Length)
{
if (_state == CsGameState.Complete)
yield break;
if (_state == CsGameState.Complete) yield break;
yield return PresentQuestion(_questions[_currentIndex]);
_currentIndex++;
if (uiManager != null)
uiManager.SetProgress(_currentIndex, _questions.Length);
uiManager?.SetProgress(_currentIndex, _questions.Length);
}
if (_state == CsGameState.Complete)
yield break;
if (_state == CsGameState.Complete) yield break;
_state = CsGameState.Complete;
yield return LoseSequence();
......@@ -298,9 +250,7 @@ namespace com.al_arcade.cs
_wrongClicks = 0;
_wrongWordButton = null;
if (bot != null)
bot.ShowSpeechBubble();
bot?.ShowSpeechBubble();
ClearWordButtons();
yield return new WaitForSeconds(sentenceShowDelay);
......@@ -308,8 +258,7 @@ namespace com.al_arcade.cs
yield return new WaitForSeconds(1.3f);
_state = CsGameState.WaitingForWordClick;
if (showHint)
uiManager.ShowHint("انقر على الكلمة الخاطئة في الجملة");
if (_showHint) uiManager?.ShowHint("انقر على الكلمة الخاطئة في الجملة");
bool questionComplete = false;
_onOptionDropped = null;
......@@ -321,8 +270,7 @@ namespace com.al_arcade.cs
bool? result = null;
_onOptionDropped = correct => result = correct;
while (result == null)
yield return null;
while (result == null) yield return null;
if (result == true)
{
......@@ -340,8 +288,7 @@ namespace com.al_arcade.cs
}
}
if (uiManager != null)
uiManager.HideOptions();
uiManager?.HideOptions();
yield return new WaitForSeconds(0.5f);
}
......@@ -351,12 +298,10 @@ namespace com.al_arcade.cs
_correctCount++;
_streak++;
_deltaChangeInSize++;
UpdateTimer(CsPrefabBuilder.Instance.correctAnswerBonusTime);
AdjustTimer(CsPrefabBuilder.Instance.correctAnswerBonusTime);
int points = Mathf.Max(100 - _wrongClicks * 15, 25);
if (_streak >= 3)
points += (_streak - 2) * 25;
if (_streak >= 3) points += (_streak - 2) * 25;
_score += points;
if (_wrongWordButton != null)
......@@ -372,26 +317,20 @@ namespace com.al_arcade.cs
SSAudioManager.Instance.PlayCorrectDrag();
yield return new WaitForSeconds(0.4f);
if (bot != null)
bot.PlayHappy();
bot?.PlayHappy();
uiManager?.HideHint();
uiManager.HideHint();
var particles = SSParticleManager.Instance;
if (particles != null && _wrongWordButton != null)
particles.PlayCorrectBurst(_wrongWordButton.transform.position);
uiManager.TickPoints(_deltaChangeInSize);
if (SSParticleManager.Instance != null && _wrongWordButton != null)
SSParticleManager.Instance.PlayCorrectBurst(_wrongWordButton.transform.position);
uiManager?.TickPoints(_deltaChangeInSize);
onAnswerGiven?.Invoke(true);
yield return new WaitForSeconds(0.8f);
if (_deltaChangeInSize == 5)
{
// Win
_state = CsGameState.Complete;
yield return VictorySequence();
yield break;
}
}
......@@ -399,12 +338,9 @@ namespace com.al_arcade.cs
{
_wrongClicks++;
_streak = 0;
_deltaChangeInSize = Mathf.Max(0, _deltaChangeInSize - 1);
_deltaChangeInSize--;
if (_deltaChangeInSize < 0)
_deltaChangeInSize = 0;
UpdateTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
AdjustTimer(-CsPrefabBuilder.Instance.wrongAnswerPenaltyTime);
if (uiManager != null)
{
......@@ -415,8 +351,7 @@ namespace com.al_arcade.cs
SSAudioManager.Instance.PlayWrong();
yield return new WaitForSeconds(0.4f);
if (bot != null)
bot.PlaySad();
bot?.PlaySad();
if (Camera.main != null)
{
......@@ -424,30 +359,60 @@ namespace com.al_arcade.cs
Camera.main.transform.DOShakePosition(0.3f, 0.15f, 10).SetId("csShake");
}
uiManager.TickPoints(_deltaChangeInSize);
uiManager?.TickPoints(_deltaChangeInSize);
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()
{
yield return new WaitForSeconds(1f);
uiManager?.ShowResults(_score, _correctCount, _wrongClicks, _questions.Length, _streak);
onGameComplete?.Invoke(_score);
ClearWordButtons();
}
protected override IEnumerator NoChallengeLoseSequence()
{
uiManager?.ShowResults(_score, _correctCount, _wrongClicks, _questions.Length, _streak, false);
yield return null;
}
protected override IEnumerator SharedLoseSequence()
{
var fullQuestion = question.words != null
? string.Join(" ", Array.ConvertAll(question.words, w => w.word_text))
: "";
SSAudioManager.Instance.PlayDefeat();
onGameComplete?.Invoke(_score);
SSAudioManager.Instance.StopMusic();
ClearWordButtons();
yield return null;
}
if (question.words == null || question.words.Length == 0)
return;
// ─── Word / UI helpers ────────────────────────────────────────────────
private void SpawnWords(CsQuestion question)
{
if (question.words == null || question.words.Length == 0) return;
ClearWordButtons();
_groupFraming.CenterOffset = new Vector2(0f, -0.1f);
// Spawn Full Sentence
var sentenceObj = new GameObject("FullQuestion");
sentenceObj.transform.SetParent(wordContainer);
var sentenceWb = sentenceObj.AddComponent<CsSentence>();
sentenceWb.Initialize(question);
return;
}
private void ShowOptions()
......@@ -456,10 +421,10 @@ namespace com.al_arcade.cs
if (uiManager != null)
{
uiManager.ShowOptions(question.options, _wrongWordButton);
if (showHint)
if (_showHint)
{
uiManager.ShowHint("اسحب الإجابة الصحيحة إلى الكلمة الخاطئة");
showHint = false;
_showHint = false;
}
}
}
......@@ -468,10 +433,8 @@ namespace com.al_arcade.cs
{
foreach (Transform child in wordContainer)
{
child.transform.DOScale(Vector3.zero, 0.3f).SetEase(Ease.InBack).OnComplete(() =>
{
Destroy(child.gameObject);
});
child.transform.DOScale(Vector3.zero, 0.3f).SetEase(Ease.InBack)
.OnComplete(() => Destroy(child.gameObject));
}
if (_targetGroup.Targets.Count > 2)
......@@ -490,91 +453,5 @@ namespace com.al_arcade.cs
_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,7 +227,10 @@ namespace com.al_arcade.cs
if (useOfflineTestData)
_gm.StartWithQuestions(GetTestQuestions());
else
_gm.StartGame();
{
if (!_gm.IsChallengeMode)
_gm.StartGame();
}
}
......
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.Collections.Generic;
using System.Linq;
using System.Reactive;
using com.al_arcade.shared;
using Cysharp.Threading.Tasks;
using EasyTransition;
using UnityEngine;
using UnityEngine.SceneManagement;
// 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.
......@@ -26,6 +25,7 @@ public class ChallengeManager : MonoBehaviour
private DateTime startTime;
IChallengeGame currentGame = null;
BaseGameManager baseGameManager = null;
[SerializeField] private ChallengeCanvas challengeCanvas;
......@@ -50,7 +50,7 @@ public class ChallengeManager : MonoBehaviour
[ContextMenu("Fake win")]
public void Fakewin()
{
OnGameCompleted(true, 30);
OnGameCompleted(true, 30, 100);
}
......@@ -64,8 +64,9 @@ public class ChallengeManager : MonoBehaviour
await LoadNextGameAndListen();
}
private void OnGameCompleted(bool hasWon, float timeLeft)
private void OnGameCompleted(bool hasWon, float timeLeft, float pointsEarned)
{
print("Game completed");
if (currentGame != null)
currentGame.OnGameCompleted -= OnGameCompleted;
......@@ -100,6 +101,12 @@ public class ChallengeManager : MonoBehaviour
return currentGame != null;
});
baseGameManager = currentGame as BaseGameManager;
await UniTask.WaitForSeconds(0.5f);
baseGameManager.ResetGame();
baseGameManager.StartGame();
currentGame.OnGameCompleted += OnGameCompleted;
}
......@@ -108,7 +115,7 @@ public class ChallengeManager : MonoBehaviour
Debug.Log("Challenge failed.");
// 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);
}
......
......@@ -3,5 +3,5 @@ using System;
public interface IChallengeGame
{
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
AnswerFeedback, BetweenQuestions, GameComplete, GameOver
}
public class McqGameManager : MonoBehaviour
public class McqGameManager : BaseGameManager
{
public static McqGameManager Instance { get; private set; }
[Header("Game Settings")]
[Header("MCQ Settings")]
[SerializeField] private float gateSpawnDistance = 50f;
[SerializeField] private float questionDisplayTime = 1.5f;
[SerializeField] private float feedbackDisplayTime = 2f;
......@@ -40,101 +40,95 @@ namespace com.al_arcade.mcq
[SerializeField] private McqGameState _state = McqGameState.Idle;
public McqGameState State => _state;
// MCQ-specific state
private McqQuestion[] _questions;
private int _currentIndex;
private int _score, _streak, _bestStreak, _lives;
private int _correctCount, _wrongCount;
private int _bestStreak, _lives;
private List<McqGateController> _activeGates = new();
private int _correctGateIndex = -1;
private Camera _mainCamera;
[Header("Events")]
public UnityEvent onGameStart;
[Header("MCQ Events")]
public UnityEvent<McqQuestion> onQuestionPresented;
public UnityEvent<bool> onAnswerGiven = new UnityEvent<bool>();
public UnityEvent<int, int> onScoreChanged;
public UnityEvent<int> onLifeLost;
public UnityEvent<int> onGameComplete;
public UnityEvent onGameOver = new UnityEvent();
private void Awake()
// ─── Singleton ────────────────────────────────────────────────────────
protected override void Awake()
{
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this;
base.Awake();
_mainCamera = Camera.main;
onAnswerGiven.AddListener(CameraFeedback);
}
public void StartGame() => StartCoroutine(StartGameRoutine());
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();
}
// ─── BaseGameManager implementation ──────────────────────────────────
protected override string GameTypeKey => "mcq";
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 api = SSApiManager.EnsureInstance();
string error = null;
yield return api.FetchMcq(
session.buildType, session.classCode,
session.questionCount, session.gradeId,
qs => _questions = qs,
err => error = err
err => onError(err)
);
}
if (error != null || _questions == null || _questions.Length == 0)
{
if (uiManager != null) uiManager.ShowError(error ?? "لا توجد أسئلة متاحة");
_state = McqGameState.Idle;
yield break;
}
protected override bool HasValidQuestions() =>
_questions != null && _questions.Length > 0;
protected override void OnShowLoading() =>
uiManager?.ShowLoading("جاري تحميل الأسئلة...");
if (uiManager != null) uiManager.HideLoading();
protected override void OnHideLoading() =>
uiManager?.HideLoading();
// Robot getting up animation
protected override void OnShowError(string message)
{
uiManager?.ShowError(message);
_state = McqGameState.Idle;
}
protected override IEnumerator OnBeforeBeginGameplay()
{
player?.SetupManager(this);
competitor?.SetupManager(this);
// Robot getting up animation
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;
_score = _streak = _bestStreak = _correctCount = _wrongCount = 0;
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
_bestStreak = 0;
_lives = totalLives;
_timerRunning = false; // MCQ doesn't use timer
if (uiManager != null)
{
......@@ -144,12 +138,39 @@ namespace com.al_arcade.mcq
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();
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()
{
while (_currentIndex < _questions.Length && _lives > 0)
......@@ -163,7 +184,7 @@ namespace com.al_arcade.mcq
if (_lives <= 0)
{
_state = McqGameState.GameOver;
yield return GameOverSequence();
yield return LoseSequence();
}
else
{
......@@ -181,7 +202,6 @@ namespace com.al_arcade.mcq
questionDisplay.Show(question.question_text, question.source);
onQuestionPresented?.Invoke(question);
// yield return new WaitForSeconds(questionDisplayTime);
_state = McqGameState.WaitingForAnswer;
......@@ -210,18 +230,12 @@ namespace com.al_arcade.mcq
yield return null;
}
/*
if (player != null) player.Stop();
if (competitor != null) competitor.Stop();
*/
foreach (var gate in _activeGates)
gate.onPlayerEnter -= OnGateHit;
_state = McqGameState.AnswerFeedback;
yield return ProcessAnswer(answered && wasCorrect);
foreach (var gate in _activeGates)
{
if (gate != null)
......@@ -236,7 +250,6 @@ namespace com.al_arcade.mcq
yield return new WaitForSeconds(0.5f);
_state = McqGameState.BetweenQuestions;
//if (player != null) yield return player.MoveToNextLane(0.4f);
if (questionDisplay != null) questionDisplay.Hide();
}
......@@ -247,9 +260,7 @@ namespace com.al_arcade.mcq
_correctCount++;
_streak++;
if (_streak > _bestStreak) _bestStreak = _streak;
int points = 100;
if (_streak >= streakBonusThreshold)
points += (_streak - (int)streakBonusThreshold + 1) * 25;
int points = CalculateStreakScore((int)streakBonusThreshold);
_score += points;
ShowCorrectFeedback(points);
......@@ -260,11 +271,11 @@ namespace com.al_arcade.mcq
if (audio.sfxCorrect != null) audio.PlayCorrect();
else audio.PlayCorrectBeep();
}
var particles = SSParticleManager.Instance;
if (particles != null && player != null)
particles.PlayCorrectBurst(player.transform.position + Vector3.up * 2f);
// Sky color
Camera.main.DOColor(SSColorPalette.CorrectWord, 1).SetEase(Ease.Flash, 2);
}
else
......@@ -280,6 +291,7 @@ namespace com.al_arcade.mcq
if (audio.sfxWrong != null) audio.PlayWrong();
else audio.PlayWrongBeep();
}
var particles = SSParticleManager.Instance;
if (particles != null && player != null)
particles.PlayWrongBurst(player.transform.position + Vector3.up * 2f);
......@@ -287,7 +299,6 @@ namespace com.al_arcade.mcq
if (uiManager != null) uiManager.SetLives(_lives, totalLives);
onLifeLost?.Invoke(_lives);
// Sky color
Camera.main.DOColor(SSColorPalette.WrongWord, 1).SetEase(Ease.Flash, 2);
}
......@@ -297,7 +308,7 @@ namespace com.al_arcade.mcq
yield return new WaitForSeconds(feedbackDisplayTime);
}
// ─── Gate Spawning ───────────────────────────────────────────────────
private void SpawnGates(McqQuestion question)
{
string[] answers = question.GetShuffledAnswers(out int correctIdx);
......@@ -308,7 +319,6 @@ namespace com.al_arcade.mcq
questionDisplay.ShowAnswers(answers);
float playerZ = player != null ? player.transform.position.z : 0f;
Vector3 basePos = new Vector3(0f, 0f, playerZ + gateSpawnDistance);
float totalWidth = (answers.Length - 1) * gateSpacing;
......@@ -347,19 +357,15 @@ namespace com.al_arcade.mcq
McqGateController gate;
if (gatePrefab == null)
{
gate = go.AddComponent<McqGateController>();
}
else
{
gate = go.GetComponent<McqGateController>();
}
gate.Setup(index, answerText, isCorrect);
return gate;
}
// ─── Feedback ────────────────────────────────────────────────────────
private void ShowCorrectFeedback(int points)
{
if (uiManager != null)
......@@ -369,6 +375,7 @@ namespace com.al_arcade.mcq
: $"صحيح! +{points}";
uiManager.ShowFeedback(msg, true);
}
foreach (var g in _activeGates)
{
if (g == null) continue;
......@@ -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;
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;
if (particles != null) particles.PlayScreenConfetti();
yield return new WaitForSeconds(1f);
}
protected override IEnumerator NoChallengeVictorySequence()
{
yield return new WaitForSeconds(0.5f);
if (uiManager != null)
uiManager.ShowResults(_score, _correctCount, _wrongCount,
......@@ -429,13 +450,21 @@ namespace com.al_arcade.mcq
onGameComplete?.Invoke(_score);
}
private IEnumerator GameOverSequence()
protected override IEnumerator SharedLoseSequence()
{
Debug.Log("Game over");
Debug.Log("MCQ game over");
var audio = SSAudioManager.Instance;
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);
if (uiManager != null)
uiManager.ShowResults(_score, _correctCount, _wrongCount,
......@@ -443,16 +472,9 @@ namespace com.al_arcade.mcq
onGameOver?.Invoke();
}
private void CameraFeedback(bool correct)
{
// If Correct Increase fov
_mainCamera.DOFieldOfView(correct ? 70f : 60f, 0.2f).SetEase(Ease.OutQuad);
}
public int Score => _score;
// ─── MCQ-specific accessors ──────────────────────────────────────────
public int Lives => _lives;
public int Streak => _streak;
public int CurrentQuestionIndex => _currentIndex;
public int BestStreak => _bestStreak;
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
guid: c7f3fdf6e62442c5485a9ff394896b54
\ No newline at end of file
guid: 42fbf78681728ed4d92e5b86342792b3
\ No newline at end of file
......@@ -6,13 +6,12 @@ using DG.Tweening;
namespace com.al_arcade.tf
{
using Microsoft.IdentityModel.Tokens;
using shared;
public enum TfGameState
{ Idle, Loading, Playing, Feedback, Complete, GameOver }
public class TfGameManager : MonoBehaviour
public class TfGameManager : BaseGameManager
{
public static TfGameManager Instance { get; private set; }
......@@ -20,6 +19,7 @@ namespace com.al_arcade.tf
[SerializeField] private int stepsToWin = 5;
[SerializeField] private float feedbackDuration = 1.2f;
[SerializeField] private float stepDistance = 3f;
[SerializeField] private float feedbackTime = 5f;
[Header("References")]
public TfHandController handController;
......@@ -29,108 +29,67 @@ namespace com.al_arcade.tf
private TfGameState _state = TfGameState.Idle;
private TfQuestion[] _questions;
private int _currentIndex, _progress, _score, _streak;
private int _correctCount, _wrongCount, _totalAsked;
private int _progress;
private bool _waitingForAnswer;
private int _pendingAnswer = -1;
private DateTime gameStartTime;
[Header("Events")]
public UnityEvent onGameStart;
public UnityEvent<bool> onAnswerGiven;
[Header("TF Events")]
public UnityEvent<int> onProgressChanged;
public UnityEvent onGameComplete;
public UnityEvent onGameOver;
// --- ADDED: Timer Settings ---
[SerializeField] private float maxTimePerQuestion = 30f;
[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()
// ─── Singleton ────────────────────────────────────────────────────────
protected override void Awake()
{
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this;
base.Awake();
}
public void StartGame()
{
StartCoroutine(StartGameRoutine());
_currentTime = maxTimePerQuestion;
_timerRunning = true;
}
public void StartWithQuestions(TfQuestion[] questions)
{ _questions = questions; BeginGameplay(); }
public void SubmitAnswer(bool answer)
{
if (!_waitingForAnswer) return;
_waitingForAnswer = false;
_pendingAnswer = answer ? 1 : 0;
}
// ─── BaseGameManager implementation ──────────────────────────────────
protected override string GameTypeKey => "tf";
public void ResetGame()
protected override IEnumerator FetchQuestions(Action<string> onError)
{
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 api = SSApiManager.EnsureInstance();
string error = null;
yield return api.FetchTf(
session.buildType, session.classCode,
session.questionCount, session.gradeId,
qs => _questions = qs,
err => error = err
err => onError(err)
);
}
if (error != null || _questions == null || _questions.Length == 0)
{
if (uiManager != null) uiManager.ShowError(error ?? "لا توجد أسئلة");
_state = TfGameState.Idle;
yield break;
}
protected override bool HasValidQuestions() =>
_questions != null && _questions.Length > 0;
if (uiManager != null) uiManager.HideLoading();
protected override void OnShowLoading() =>
uiManager?.ShowLoading("جاري تحميل الأسئلة...");
gameStartTime = DateTime.Now;
BeginGameplay();
protected override void OnHideLoading() =>
uiManager?.HideLoading();
protected override void OnShowError(string message)
{
uiManager?.ShowError(message);
_state = TfGameState.Idle;
}
private void BeginGameplay()
protected override void OnTimerTick(float timeLeft) =>
uiManager?.SetTimer(timeLeft);
protected override IEnumerator OnTimeUp()
{
yield return HandleTimeUp();
}
protected override void BeginGameplay()
{
_currentIndex = _progress = _score = _streak = 0;
_correctCount = _wrongCount = _totalAsked = 0;
_progress = 0;
// Reset base counters
_currentIndex = _score = _streak = _correctCount = _wrongCount = _totalAsked = 0;
_state = TfGameState.Playing;
if (uiManager != null)
......@@ -144,40 +103,40 @@ namespace com.al_arcade.tf
StartCoroutine(GameLoop());
}
private void Update()
// ─── Public API ───────────────────────────────────────────────────────
public void StartWithQuestions(TfQuestion[] questions)
{
if (!_timerRunning) return;
_currentTime -= Time.deltaTime;
if (uiManager != null)
uiManager.SetTimer(_currentTime);
if (_currentTime <= 0f)
{
_currentTime = 0f;
_timerRunning = false;
StartCoroutine(HandleTimeUp());
}
_questions = questions;
BeginGameplay();
}
private IEnumerator HandleTimeUp()
{
Debug.Log("Handle Time UP");
_state = TfGameState.GameOver;
_timerRunning = false;
public void SubmitAnswer(bool answer)
{
if (!_waitingForAnswer) return;
_waitingForAnswer = false;
_pendingAnswer = answer ? 1 : 0;
}
if (productionLine != null && productionLine.machineVFXController != null)
productionLine.machineVFXController.PlayExplosion();
public void ResetGame()
{
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)
uiManager.ShowResults(_score, _correctCount, _wrongCount, false);
// ─── Timer feedback override ──────────────────────────────────────────
// TF adjusts time on correct/wrong, so it needs direct access
public float GetTimeLeft() => _timeLeft;
onGameOver?.Invoke();
}
// ─── Game loop ────────────────────────────────────────────────────────
private IEnumerator GameLoop()
{
while (_progress < stepsToWin && _currentIndex < _questions.Length)
......@@ -189,13 +148,11 @@ namespace com.al_arcade.tf
_waitingForAnswer = true;
_pendingAnswer = -1;
_state = TfGameState.Playing;
// --- ADDED: Start timer for this question ---
if (handController != null) handController.SetReady(true);
while (_pendingAnswer < 0)
{
if (Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A))
SubmitAnswer(true);
if (Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D))
......@@ -215,7 +172,7 @@ namespace com.al_arcade.tf
_correctCount++;
_streak++;
_progress++;
_score += 100 + (_streak > 2 ? (_streak - 2) * 25 : 0);
_score += CalculateStreakScore(); // shared helper
if (handController != null) handController.PlayCorrectFeedback(playerSaidTrue);
if (productionLine != null) yield return productionLine.MoveForward(stepDistance);
......@@ -226,10 +183,11 @@ namespace com.al_arcade.tf
{ if (audio.sfxCorrect != null) audio.PlayCorrect(); else audio.PlayCorrectBeep(); }
var particles = SSParticleManager.Instance;
if (particles != null && Camera.main != null)
if (particles != null)
particles.PlayCorrectBurst(new Vector3(-2, 6f, 13));
_currentTime += 4;
uiManager?.UpdateTimer(_currentTime, true);
UpdateTimerBy(4f); // shared helper
uiManager?.UpdateTimer(_timeLeft, true);
}
else
{
......@@ -251,12 +209,14 @@ namespace com.al_arcade.tf
Camera.main.transform.DOShakePosition(0.4f, 0.3f, 15)
.SetEase(Ease.OutQuad).SetId("camShake");
}
_currentTime -= 2;
uiManager?.UpdateTimer(_currentTime, false);
UpdateTimerBy(-2f); // shared helper
uiManager?.UpdateTimer(_timeLeft, false);
}
onAnswerGiven?.Invoke(isCorrect);
onProgressChanged?.Invoke(_progress);
if (uiManager != null)
{
uiManager.SetProgress(_progress, stepsToWin);
......@@ -276,11 +236,31 @@ namespace com.al_arcade.tf
}
}
private IEnumerator VictorySequence()
// ─── End sequences ────────────────────────────────────────────────────
private IEnumerator HandleTimeUp()
{
Debug.Log("victorySequance");
_state = TfGameState.GameOver;
yield return LoseSequence();
}
protected override IEnumerator SharedLoseSequence()
{
if (productionLine?.machineVFXController != null)
productionLine.machineVFXController.PlayExplosion();
yield return null;
}
protected override IEnumerator NoChallengeLoseSequence()
{
yield return new WaitForSeconds(1f);
uiManager?.ShowResults(_score, _correctCount, _wrongCount, false);
onGameOver?.Invoke();
}
protected override IEnumerator SharedVictorySequence()
{
_timerRunning = false;
var audio = SSAudioManager.Instance;
if (audio != null)
{ if (audio.sfxVictory != null) audio.PlayVictory(); else audio.PlaySuccessJingle(); }
......@@ -290,17 +270,18 @@ namespace com.al_arcade.tf
if (productionLine != null) yield return productionLine.ProductComplete();
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 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:
webWasm2023: 0
webEnableSubmoduleStrippingCompatibility: 0
scriptingDefineSymbols:
Android: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
Android: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2;UNITEXT
EmbeddedLinux: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
GameCoreScarlett: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
GameCoreXboxOne: DOTWEEN;UNITY_POST_PROCESSING_STACK_V2
......
......@@ -33,13 +33,13 @@ EditorUserSettings:
value: 5155075f06575f0a0f080e7a47270e444116497f782b7e367c7e4d6abbb9656a
flags: 0
RecentlyUsedSceneGuid-6:
value: 0752035101010f0c54595b2046760e44134e4e7a7f7d71677c2c4836b7b4633e
value: 5701055506000a030f5c542744260844404f4d73797975367c2c1e6ab7e2653d
flags: 0
RecentlyUsedSceneGuid-7:
value: 5701055506000a030f5c542744260844404f4d73797975367c2c1e6ab7e2653d
value: 52080c51560d5f03580b5e7242700c4446164f7d2e7f77612c281f32e0b8603d
flags: 0
RecentlyUsedSceneGuid-8:
value: 52080c51560d5f03580b5e7242700c4446164f7d2e7f77612c281f32e0b8603d
value: 0752035101010f0c54595b2046760e44134e4e7a7f7d71677c2c4836b7b4633e
flags: 0
RecentlyUsedSceneGuid-9:
value: 060203560401505a595d0a7345200d44404e1b7e2d707e617b7f4d63e7b6606b
......
......@@ -19,12 +19,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 36
width: 1918
height: 932
width: 1920
height: 941
m_MinSize: {x: 300, y: 112}
m_MaxSize: {x: 24288, y: 16192}
vertical: 0
controlID: 2275
controlID: 39
draggingID: 0
--- !u!114 &2
MonoBehaviour:
......@@ -47,10 +47,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Game\u200B"
m_Pos:
serializedVersion: 2
x: 357
y: 87
width: 1105
height: 573
x: 356
y: 79
width: 1107
height: 579
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......@@ -78,8 +78,8 @@ MonoBehaviour:
m_UseMipMap: 0
m_VSyncEnabled: 0
m_Gizmos: 0
m_Stats: 1
m_SelectedSizes: 07000000000000000000000000000000000000000000000000000000000000000000000000000000
m_Stats: 0
m_SelectedSizes: 07000000000000000000000012000000000000000000000000000000000000000000000000000000
m_ZoomArea:
m_HRangeLocked: 0
m_VRangeLocked: 0
......@@ -106,26 +106,26 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 21
width: 1105
height: 552
m_Scale: {x: 0.23, y: 0.23}
m_Translation: {x: 552.5, y: 276}
width: 1107
height: 558
m_Scale: {x: 0.23249999, y: 0.2325}
m_Translation: {x: 553.5, y: 279}
m_MarginLeft: 0
m_MarginRight: 0
m_MarginTop: 0
m_MarginBottom: 0
m_LastShownAreaInsideMargins:
serializedVersion: 2
x: -2402.1738
x: -2380.6453
y: -1200
width: 4804.3477
width: 4761.2905
height: 2400
m_MinimalGUI: 1
m_defaultScale: 0.23
m_LastWindowPixelSize: {x: 1105, y: 573}
m_defaultScale: 0.2325
m_LastWindowPixelSize: {x: 1107, y: 579}
m_ClearInEditMode: 1
m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000000000000000000
m_LowResolutionForAspectRatios: 01000001000000000000
m_XRRenderMode: 0
m_RenderTexture: {fileID: 0}
m_showToolbar: 1
......@@ -148,12 +148,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1463
height: 932
width: 1465
height: 941
m_MinSize: {x: 200, y: 112}
m_MaxSize: {x: 16192, y: 16192}
vertical: 1
controlID: 2276
controlID: 40
draggingID: 0
--- !u!114 &4
MonoBehaviour:
......@@ -174,12 +174,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1463
height: 599
width: 1465
height: 605
m_MinSize: {x: 200, y: 56}
m_MaxSize: {x: 16192, y: 8096}
vertical: 0
controlID: 2277
controlID: 41
draggingID: 0
--- !u!114 &5
MonoBehaviour:
......@@ -199,7 +199,7 @@ MonoBehaviour:
x: 0
y: 0
width: 356
height: 599
height: 605
m_MinSize: {x: 201, y: 226}
m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 6}
......@@ -228,10 +228,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Hierarchy\u200B"
m_Pos:
serializedVersion: 2
x: 1
y: 87
x: 0
y: 79
width: 355
height: 573
height: 579
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......@@ -248,17 +248,11 @@ MonoBehaviour:
m_SceneHierarchy:
m_TreeViewState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs:
- m_Data: 56538
m_SelectedIDs: []
m_LastClickedID:
m_Data: 56538
m_Data: 0
m_ExpandedIDs:
- m_Data: -1346
- m_Data: 56482
- m_Data: 56500
- m_Data: 56528
- m_Data: 56538
- m_Data: 56548
- m_Data: -1344
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -301,8 +295,8 @@ MonoBehaviour:
serializedVersion: 2
x: 356
y: 0
width: 1107
height: 599
width: 1109
height: 605
m_MinSize: {x: 202, y: 226}
m_MaxSize: {x: 4002, y: 4026}
m_ActualView: {fileID: 2}
......@@ -1024,9 +1018,9 @@ MonoBehaviour:
m_Position:
serializedVersion: 2
x: 0
y: 599
width: 1463
height: 333
y: 605
width: 1465
height: 336
m_MinSize: {x: 101, y: 126}
m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 11}
......@@ -1087,7 +1081,7 @@ MonoBehaviour:
m_SkipHidden: 0
m_SearchArea: 1
m_Folders:
- Assets/App/Core
- Assets/AppUI/Scenes
m_Globs: []
m_ProductIds:
m_AnyWithAssetOrigin: 0
......@@ -1097,7 +1091,7 @@ MonoBehaviour:
m_ViewMode: 1
m_StartGridSize: 96
m_LastFolders:
- Assets/App/Core
- Assets/AppUI/Scenes
m_LastFoldersGridSize: 96
m_LastProjectPath: D:\Dev\Projects\SSBookMinigames\My project
m_LockTracker:
......@@ -1106,14 +1100,12 @@ MonoBehaviour:
m_FolderTreeState:
scrollPos: {x: 0, y: 79}
m_SelectedIDs:
- m_Data: 57168
- m_Data: 74206
m_LastClickedID:
m_Data: 57168
m_Data: 74206
m_ExpandedIDs:
- m_Data: 0
- m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -1147,8 +1139,6 @@ MonoBehaviour:
m_ExpandedIDs:
- m_Data: 0
- m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -1176,8 +1166,8 @@ MonoBehaviour:
m_ResourceFile:
m_ListAreaState:
m_SelectedInstanceIDs:
- m_Data: 56474
m_LastClickedInstanceID: 56474
- m_Data: -165430
m_LastClickedInstanceID: -165430
m_HadKeyboardFocusLastEvent: 0
m_ExpandedInstanceIDs:
- m_Data: 46526
......@@ -1233,10 +1223,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Console\u200B"
m_Pos:
serializedVersion: 2
x: 1
y: 686
width: 1462
height: 307
x: 0
y: 684
width: 1464
height: 310
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......@@ -1290,7 +1280,7 @@ MonoBehaviour:
m_DynamicPanelBehavior: 0
m_LockTracker:
m_IsLocked: 0
m_LastSelectedObjectID: 56538
m_LastSelectedObjectID: 0
--- !u!114 &13
MonoBehaviour:
m_ObjectHideFlags: 52
......@@ -1420,8 +1410,6 @@ MonoBehaviour:
m_ExpandedIDs:
- m_Data: 0
- m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -1455,8 +1443,6 @@ MonoBehaviour:
m_ExpandedIDs:
- m_Data: 0
- m_Data: 56686
- m_Data: 1000000000
- m_Data: 2147483647
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -1531,10 +1517,10 @@ MonoBehaviour:
m_Children: []
m_Position:
serializedVersion: 2
x: 1463
x: 1465
y: 0
width: 455
height: 932
height: 941
m_MinSize: {x: 276, y: 76}
m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 16}
......@@ -1563,10 +1549,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Inspector\u200B"
m_Pos:
serializedVersion: 2
x: 1464
y: 87
x: 1465
y: 79
width: 454
height: 906
height: 915
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......
......@@ -14,16 +14,16 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.ContainerWindow
m_PixelRect:
serializedVersion: 2
x: 1
y: 51
width: 1918
height: 988
x: 0
y: 43
width: 1920
height: 997
m_ShowMode: 4
m_Title: Project
m_Title: Hierarchy
m_RootView: {fileID: 2}
m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000}
m_Maximized: 0
m_Maximized: 1
--- !u!114 &2
MonoBehaviour:
m_ObjectHideFlags: 52
......@@ -44,8 +44,8 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1918
height: 988
width: 1920
height: 997
m_MinSize: {x: 875, y: 300}
m_MaxSize: {x: 10000, y: 10000}
m_UseTopView: 1
......@@ -69,7 +69,7 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1918
width: 1920
height: 36
m_MinSize: {x: 50, y: 50}
m_MaxSize: {x: 4000, y: 4000}
......@@ -90,8 +90,8 @@ MonoBehaviour:
m_Position:
serializedVersion: 2
x: 0
y: 968
width: 1918
y: 977
width: 1920
height: 20
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
......@@ -114,12 +114,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 36
width: 1918
height: 932
width: 1920
height: 941
m_MinSize: {x: 300, y: 112}
m_MaxSize: {x: 24288, y: 16192}
vertical: 0
controlID: 1755
controlID: 160
draggingID: 0
--- !u!114 &6
MonoBehaviour:
......@@ -140,12 +140,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1463
height: 932
width: 1465
height: 941
m_MinSize: {x: 200, y: 112}
m_MaxSize: {x: 16192, y: 16192}
vertical: 1
controlID: 1756
controlID: 161
draggingID: 0
--- !u!114 &7
MonoBehaviour:
......@@ -166,12 +166,12 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 1463
height: 599
width: 1465
height: 605
m_MinSize: {x: 200, y: 56}
m_MaxSize: {x: 16192, y: 8096}
vertical: 0
controlID: 1757
controlID: 162
draggingID: 0
--- !u!114 &8
MonoBehaviour:
......@@ -190,8 +190,8 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 0
width: 356
height: 599
width: 355
height: 605
m_MinSize: {x: 201, y: 226}
m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 14}
......@@ -214,10 +214,10 @@ MonoBehaviour:
m_Children: []
m_Position:
serializedVersion: 2
x: 356
x: 355
y: 0
width: 1107
height: 599
width: 1110
height: 605
m_MinSize: {x: 202, y: 226}
m_MaxSize: {x: 4002, y: 4026}
m_ActualView: {fileID: 13}
......@@ -236,26 +236,26 @@ MonoBehaviour:
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name: ProjectBrowser
m_Name: ConsoleWindow
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 599
width: 1463
height: 333
m_MinSize: {x: 231, y: 276}
m_MaxSize: {x: 10001, y: 10026}
m_ActualView: {fileID: 16}
y: 605
width: 1465
height: 336
m_MinSize: {x: 101, y: 126}
m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 17}
m_Panes:
- {fileID: 16}
- {fileID: 17}
- {fileID: 18}
- {fileID: 19}
- {fileID: 20}
m_Selected: 0
m_LastSelected: 1
m_Selected: 1
m_LastSelected: 0
--- !u!114 &11
MonoBehaviour:
m_ObjectHideFlags: 52
......@@ -271,10 +271,10 @@ MonoBehaviour:
m_Children: []
m_Position:
serializedVersion: 2
x: 1463
x: 1465
y: 0
width: 455
height: 932
height: 941
m_MinSize: {x: 276, y: 76}
m_MaxSize: {x: 4001, y: 4026}
m_ActualView: {fileID: 21}
......@@ -304,8 +304,8 @@ MonoBehaviour:
m_Pos:
serializedVersion: 2
x: 0
y: 0
width: 1918
y: 43
width: 1920
height: 36
m_SerializedDataModeController:
m_DataMode: 0
......@@ -583,10 +583,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Game\u200B"
m_Pos:
serializedVersion: 2
x: 357
y: 24
width: 1105
height: 573
x: 355
y: 79
width: 1108
height: 579
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......@@ -614,8 +614,8 @@ MonoBehaviour:
m_UseMipMap: 0
m_VSyncEnabled: 0
m_Gizmos: 0
m_Stats: 1
m_SelectedSizes: 07000000000000000000000000000000000000000000000000000000000000000000000000000000
m_Stats: 0
m_SelectedSizes: 07000000000000000000000012000000000000000000000000000000000000000000000000000000
m_ZoomArea:
m_HRangeLocked: 0
m_VRangeLocked: 0
......@@ -642,26 +642,26 @@ MonoBehaviour:
serializedVersion: 2
x: 0
y: 21
width: 1105
height: 552
m_Scale: {x: 0.23, y: 0.23}
m_Translation: {x: 552.5, y: 276}
width: 1108
height: 558
m_Scale: {x: 0.2325, y: 0.2325}
m_Translation: {x: 554, y: 279}
m_MarginLeft: 0
m_MarginRight: 0
m_MarginTop: 0
m_MarginBottom: 0
m_LastShownAreaInsideMargins:
serializedVersion: 2
x: -2402.1738
x: -2382.7957
y: -1200
width: 4804.3477
width: 4765.5913
height: 2400
m_MinimalGUI: 1
m_defaultScale: 0.23
m_LastWindowPixelSize: {x: 1105, y: 573}
m_defaultScale: 0.2325
m_LastWindowPixelSize: {x: 1108, y: 579}
m_ClearInEditMode: 1
m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000000000000000000
m_LowResolutionForAspectRatios: 01000001000000000000
m_XRRenderMode: 0
m_RenderTexture: {fileID: 0}
m_showToolbar: 1
......@@ -687,9 +687,9 @@ MonoBehaviour:
m_Pos:
serializedVersion: 2
x: 0
y: 24
width: 355
height: 573
y: 79
width: 354
height: 579
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......@@ -706,16 +706,14 @@ MonoBehaviour:
m_SceneHierarchy:
m_TreeViewState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: []
m_SelectedIDs:
- m_Data: -2616
m_LastClickedID:
m_Data: 0
m_Data: -2616
m_ExpandedIDs:
- m_Data: -3222
- m_Data: -3132
- m_Data: -1346
- m_Data: 56478
- m_Data: 56508
- m_Data: 56518
- m_Data: -2614
- m_Data: -1344
- m_Data: -12
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
......@@ -1459,8 +1457,8 @@ MonoBehaviour:
m_TextWithWhitespace: "Project\u200B"
m_Pos:
serializedVersion: 2
x: 0
y: 623
x: 1
y: 686
width: 1462
height: 307
m_SerializedDataModeController:
......@@ -1487,7 +1485,7 @@ MonoBehaviour:
m_SkipHidden: 0
m_SearchArea: 1
m_Folders:
- Assets/App/Core
- Assets/AppUI/Scenes
m_Globs: []
m_ProductIds:
m_AnyWithAssetOrigin: 0
......@@ -1497,7 +1495,7 @@ MonoBehaviour:
m_ViewMode: 1
m_StartGridSize: 96
m_LastFolders:
- Assets/App/Core
- Assets/AppUI/Scenes
m_LastFoldersGridSize: 96
m_LastProjectPath: D:\Dev\Projects\SSBookMinigames\My project
m_LockTracker:
......@@ -1506,9 +1504,9 @@ MonoBehaviour:
m_FolderTreeState:
scrollPos: {x: 0, y: 79}
m_SelectedIDs:
- m_Data: 60184
- m_Data: 74206
m_LastClickedID:
m_Data: 60184
m_Data: 74206
m_ExpandedIDs:
- m_Data: 0
- m_Data: 56686
......@@ -1572,9 +1570,9 @@ MonoBehaviour:
m_ResourceFile:
m_ListAreaState:
m_SelectedInstanceIDs:
- m_Data: 60216
m_LastClickedInstanceID: 60216
m_HadKeyboardFocusLastEvent: 1
- m_Data: -165430
m_LastClickedInstanceID: -165430
m_HadKeyboardFocusLastEvent: 0
m_ExpandedInstanceIDs:
- m_Data: 46526
- m_Data: 61214
......@@ -1629,10 +1627,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Console\u200B"
m_Pos:
serializedVersion: 2
x: 1
y: 686
width: 1462
height: 307
x: 0
y: 684
width: 1464
height: 310
m_SerializedDataModeController:
m_DataMode: 0
m_PreferredDataMode: 0
......@@ -1929,10 +1927,10 @@ MonoBehaviour:
m_TextWithWhitespace: "Inspector\u200B"
m_Pos:
serializedVersion: 2
x: 1464
y: 24
x: 1465
y: 79
width: 454
height: 906
height: 915
m_SerializedDataModeController:
m_DataMode: 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