using System.Collections; using System.Collections.Generic; using Core.Domain; using Core.Ports; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; namespace Infrastructure.Unity { public class GameBootstrap : MonoBehaviour { [Header("Infrastructure")] [SerializeField] private LevelGenerator levelGenerator; [SerializeField] private OrbViewAdapter orbPrefab; [SerializeField] private PlayerController playerPrefab; [SerializeField] private DeathPlaneAdapter deathPlanePrefab; [SerializeField] private SoundManager soundManager; [SerializeField] private CameraController cameraController; [SerializeField] private NpcController npcPrefab; [Header("Level Generation")] [SerializeField] private int floorsCount = 3; [SerializeField] private float floorHeightDistance = 15f; [Header("Ui")] [SerializeField] private TMP_Text scoreText; [SerializeField] private TMP_Text highScoreText; [SerializeField] private GameObject gameOverUi; [SerializeField] private GameObject startScreenUi; [Header("Settings")] [SerializeField] private float restartTime = 3f; [Header("Power Ups")] [SerializeField] private PowerUpViewAdapter lightFootedPrefab; [SerializeField] private PowerUpViewAdapter speedBoostPrefab; private readonly List _allTiles = new(); private readonly Dictionary _tileViews = new(); private GameSession _gameSession; private IPersistenceService _persistenceService; private InputSystem_Actions _actions; private PlayerController _playerInstance; private GameObject _currentOrbInstance; private bool _isGameRunning; private void OnEnable() { _actions = new InputSystem_Actions(); _actions.Player.Enable(); } private void OnDisable() { _actions.Player.Disable(); } private void Start() { if (levelGenerator) levelGenerator.Generate(soundManager, _allTiles, _tileViews); SpawnDeathPlane(); SpawnPlayer(); if (gameOverUi) gameOverUi.SetActive(false); if (startScreenUi) startScreenUi.SetActive(true); _persistenceService = new PlayerPrefsPersistenceAdapter(); _gameSession = new GameSession(_allTiles, _persistenceService); WireEvents(); UpdateScoreUi(_gameSession.Score); } private void Update() { if (!_isGameRunning) { if (_actions.Player.StartGame.triggered) { StartGameSequence(); } return; } if (_playerInstance) { var playerY = _playerInstance.transform.position.y; var currentFloor = Mathf.RoundToInt(-playerY / floorHeightDistance); currentFloor = Mathf.Clamp(currentFloor, 0, floorsCount - 1); _gameSession.SetPlayerFloor(currentFloor); } var dt = Time.deltaTime; if (_isGameRunning) _gameSession.Tick(dt); for (var i = _allTiles.Count - 1; i >= 0; i--) { _allTiles[i].Tick(dt); } } private void SpawnVisualOrb(string tileId) { if (_currentOrbInstance) Destroy(_currentOrbInstance); if (!_tileViews.TryGetValue(tileId, out var tileView)) return; if (!tileView) return; var spawnPos = tileView.transform.position + Vector3.up; var orb = Instantiate(orbPrefab, spawnPos, Quaternion.identity); orb.OnCollected += () => _gameSession.OrbCollected(); _currentOrbInstance = orb.gameObject; } private void HandleOrbReset() { if (_currentOrbInstance) { Destroy(_currentOrbInstance); } } private void UpdateScoreUi(int newScore) { if (!scoreText) return; scoreText.text = $"Data: {newScore}"; if (highScoreText) highScoreText.text = $"BEST: {_gameSession.HighScore}"; } private void SpawnPlayer() { var spawnPos = new Vector3(0f, 5f, 0f); _playerInstance = Instantiate(playerPrefab, spawnPos, Quaternion.identity); _playerInstance.enabled = false; _playerInstance.Rigidbody.isKinematic = true; if (cameraController) { cameraController.SetTarget(_playerInstance.transform); } } private void SpawnDeathPlane() { if (!levelGenerator) return; var lowestY = -(levelGenerator.FloorsCount * levelGenerator.FloorHeightDistance) - 5f; var pos = new Vector3(levelGenerator.GridSizeX / 2f, lowestY, levelGenerator.GridSizeY / 2f); var plane = Instantiate(deathPlanePrefab, pos, Quaternion.identity); plane.transform.localScale = new Vector3(levelGenerator.GridSizeX * 2f, 1f, levelGenerator.GridSizeY * 2f); plane.OnPlayerFell += () => _gameSession.EndGame(); } private void HandleGameOver() { _isGameRunning = false; if (gameOverUi) gameOverUi.SetActive(true); StartCoroutine(RestartRoutine()); } private IEnumerator RestartRoutine() { yield return new WaitForSeconds(restartTime); SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } private void WireEvents() { _gameSession.OnScoreChanged += UpdateScoreUi; _gameSession.OnOrbSpawned += SpawnVisualOrb; _gameSession.OnOrbReset += HandleOrbReset; _gameSession.OnGameOver += HandleGameOver; _gameSession.OnSpawnNpc += SpawnNpc; _gameSession.OnSpawnPowerUp += SpawnPowerUp; if (!soundManager) return; _gameSession.OnScoreChanged += _ => soundManager.PlayScore(); _gameSession.OnGameOver += () => { soundManager.StopMusic(); soundManager.PlayGameOver(); }; } private void SpawnNpc() { if (!npcPrefab) return; var spawnPos = new Vector3(levelGenerator.GridSizeX / 2f, 7f, levelGenerator.GridSizeY / 2f); Instantiate(npcPrefab, spawnPos, Quaternion.identity); soundManager.PlayNpcSpawn(); } private void StartGameSequence() { _isGameRunning = true; if (startScreenUi) startScreenUi.SetActive(false); if (soundManager) { soundManager.PlayGameStart(); soundManager.PlayMusic(); } if (_playerInstance) { _playerInstance.enabled = true; _playerInstance.Rigidbody.isKinematic = false; } _gameSession.StartGame(); } private void SpawnPowerUp(PowerUpType type, string tileId) { if (!_tileViews.TryGetValue(tileId, out var tileView)) return; if (!tileView) return; var spawnPos = tileView.transform.position + Vector3.up * 0.5f; var prefabToSpawn = type == PowerUpType.LightFooted ? lightFootedPrefab : speedBoostPrefab; if (!prefabToSpawn) return; var instance = Instantiate(prefabToSpawn, spawnPos, Quaternion.identity); instance.Configure(type); } } }