295 lines
9.4 KiB
C#
295 lines
9.4 KiB
C#
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 RumbleManager rumbleManager;
|
|
[SerializeField] private BeatPulseController beatPulseController;
|
|
[SerializeField] private CameraController cameraController;
|
|
[SerializeField] private NpcController npcPrefab;
|
|
[SerializeField] private HunterNpcController hunterNpcPrefab;
|
|
|
|
[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 powerUpPrefab;
|
|
|
|
private readonly List<Tile> _allTiles = new();
|
|
private readonly Dictionary<string, TileViewAdapter> _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()
|
|
{
|
|
_persistenceService = new PlayerPrefsPersistenceAdapter();
|
|
_gameSession = new GameSession(_allTiles, _persistenceService);
|
|
|
|
// Set Theme based on High Score
|
|
ThemeManager.CurrentTheme = ThemeManager.GetTheme(_gameSession.HighScore);
|
|
|
|
if (levelGenerator) levelGenerator.Generate(soundManager, _allTiles, _tileViews, cameraController, rumbleManager);
|
|
|
|
SpawnDeathPlane();
|
|
SpawnPlayer();
|
|
|
|
if (gameOverUi) gameOverUi.SetActive(false);
|
|
if (startScreenUi) startScreenUi.SetActive(true);
|
|
|
|
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);
|
|
|
|
var dilation = _gameSession.TimeDilation;
|
|
|
|
// Hard Mode: Decay faster as score increases
|
|
var decayMultiplier = 1.0f + (_gameSession.Score / 500f);
|
|
|
|
for (var i = _allTiles.Count - 1; i >= 0; i--)
|
|
{
|
|
_allTiles[i].Tick(dt * dilation * decayMultiplier);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
var combo = _gameSession?.ComboMultiplier ?? 1;
|
|
var comboText = combo > 1 ? $" (x{combo})" : "";
|
|
|
|
scoreText.text = $"Data: {newScore}{comboText}";
|
|
|
|
if (highScoreText && _gameSession != null) 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 (beatPulseController) beatPulseController.StopTracking();
|
|
|
|
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()
|
|
{
|
|
var validTiles = _allTiles.FindAll(t => t.Floor == 0 && t.CurrentState == TileState.Stable);
|
|
|
|
if (validTiles.Count == 0)
|
|
{
|
|
validTiles = _allTiles.FindAll(t => t.CurrentState == TileState.Stable);
|
|
}
|
|
|
|
if (validTiles.Count == 0) return;
|
|
|
|
var randomTile = validTiles[Random.Range(0, validTiles.Count)];
|
|
|
|
if (!_tileViews.TryGetValue(randomTile.Id, out var tileView)) return;
|
|
if (!tileView) return;
|
|
|
|
var spawnPos = tileView.transform.position + Vector3.up * 5f;
|
|
|
|
if (_playerInstance && hunterNpcPrefab && Random.value < 0.3f)
|
|
{
|
|
var hunter = Instantiate(hunterNpcPrefab, spawnPos, Quaternion.identity);
|
|
hunter.Initialize(_playerInstance);
|
|
}
|
|
else if (npcPrefab)
|
|
{
|
|
Instantiate(npcPrefab, spawnPos, Quaternion.identity);
|
|
}
|
|
|
|
soundManager.PlayNpcSpawn();
|
|
}
|
|
|
|
private void StartGameSequence()
|
|
{
|
|
_isGameRunning = true;
|
|
|
|
if (startScreenUi) startScreenUi.SetActive(false);
|
|
|
|
if (soundManager)
|
|
{
|
|
soundManager.PlayGameStart();
|
|
soundManager.PlayMusic();
|
|
|
|
if (beatPulseController) beatPulseController.BeginTracking();
|
|
}
|
|
|
|
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 instance = Instantiate(powerUpPrefab, spawnPos, Quaternion.identity);
|
|
|
|
instance.Configure(type);
|
|
|
|
instance.OnCollected += (t) =>
|
|
{
|
|
cameraController?.Shake(0.2f, 0.15f);
|
|
rumbleManager?.PulseMedium();
|
|
|
|
if (t == PowerUpType.TimeSlow)
|
|
{
|
|
_gameSession.ActivateTimeSlow(10f);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
} |