Compare commits
15 Commits
17e3425de3
...
2d54920df9
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d54920df9 | |||
| 03bfb16cf3 | |||
| 65af5ad2eb | |||
| eedbbb2b47 | |||
| 3c6e309886 | |||
| 2bfc2ea9c2 | |||
| 1b8c7f730d | |||
| 8edb5cfbb5 | |||
| f507707251 | |||
| 67df6bf6d6 | |||
| 49c9a7904d | |||
| 34a329ad02 | |||
| d4dc30bd7a | |||
| feed4da28c | |||
| 0704f2e0a0 |
8
Assets/Data.meta
Normal file
8
Assets/Data.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0d581912a9a00ddd90532c3da3be7fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/Data/Level.asset
Normal file
21
Assets/Data/Level.asset
Normal file
@@ -0,0 +1,21 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d1e67659d937881ee99b3b9e84f91428, type: 3}
|
||||
m_Name: Level
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Infrastructure.Unity.LevelDefinition
|
||||
gridSizeX: 40
|
||||
gridSizeY: 40
|
||||
floorHeightDistance: 15
|
||||
floors:
|
||||
- pattern: 0
|
||||
- pattern: 1
|
||||
- pattern: 2
|
||||
8
Assets/Data/Level.asset.meta
Normal file
8
Assets/Data/Level.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b72a68ad35f58fc3baa8043b5dfb42c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because one or more lines are too long
@@ -610,6 +610,7 @@ GameObject:
|
||||
- component: {fileID: 453022423}
|
||||
- component: {fileID: 453022424}
|
||||
- component: {fileID: 453022425}
|
||||
- component: {fileID: 453022426}
|
||||
m_Layer: 0
|
||||
m_Name: Game
|
||||
m_TagString: Untagged
|
||||
@@ -640,11 +641,7 @@ MonoBehaviour:
|
||||
npcPrefab: {fileID: 6083523108754401876, guid: 4b3d84858334857368bde30df360ae3e, type: 3}
|
||||
hunterNpcPrefab: {fileID: 4496988857626767934, guid: ab4e193839fef9a2189f27360914c044, type: 3}
|
||||
floorVisibilityManager: {fileID: 453022425}
|
||||
scoreText: {fileID: 412275999}
|
||||
highScoreText: {fileID: 1626199842}
|
||||
gameOverUi: {fileID: 87831902}
|
||||
pauseUi: {fileID: 1019087904}
|
||||
startScreenUi: {fileID: 1763855010}
|
||||
uiCoordinator: {fileID: 453022426}
|
||||
restartTime: 3
|
||||
powerUpPrefab: {fileID: 7381336953128067686, guid: 8b540be4548e610709c2f7eccf8bf9c6, type: 3}
|
||||
--- !u!4 &453022421
|
||||
@@ -678,10 +675,7 @@ MonoBehaviour:
|
||||
tileBreakVfxPrefab: {fileID: 0}
|
||||
jumpPadPrefab: {fileID: 3258547662887829175, guid: e1d1bd44370c9986ebd4bb7730430a12, type: 3}
|
||||
teleporterPrefab: {fileID: 4601941687390792571, guid: 53f1de555c523511e9aaa1dee06fdf79, type: 3}
|
||||
gridSizeX: 25
|
||||
gridSizeY: 25
|
||||
floorsCount: 3
|
||||
floorHeightDistance: 15
|
||||
levelDefinition: {fileID: 11400000, guid: 5b72a68ad35f58fc3baa8043b5dfb42c, type: 2}
|
||||
decayTime: 0.75
|
||||
fallingTime: 2
|
||||
minimumDistanceBetweenTeleporters: 10
|
||||
@@ -780,6 +774,23 @@ MonoBehaviour:
|
||||
fadeSpeed: 5
|
||||
hiddenAlpha: 0.1
|
||||
visibleAlpha: 1
|
||||
--- !u!114 &453022426
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 453022419}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b2d628098b45e36879d59d2c2c2bf061, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Infrastructure.Unity.GameUiCoordinator
|
||||
scoreText: {fileID: 412275999}
|
||||
highScoreText: {fileID: 1626199842}
|
||||
gameOverUi: {fileID: 87831902}
|
||||
pauseUi: {fileID: 1019087904}
|
||||
startScreenUi: {fileID: 1763855010}
|
||||
--- !u!1 &832575517
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Core.Ports;
|
||||
using UnityEngine;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace Core.Domain
|
||||
@@ -25,7 +25,7 @@ namespace Core.Domain
|
||||
public event Action OnSpawnNpc;
|
||||
public event Action<PowerUpType, string> OnSpawnPowerUp;
|
||||
|
||||
private readonly List<Tile> _tiles;
|
||||
private readonly IReadOnlyList<Tile> _tiles;
|
||||
private readonly IPersistenceService _persistenceService;
|
||||
private readonly Random _rng = new();
|
||||
private int _playerFloorIndex = 0;
|
||||
@@ -40,7 +40,7 @@ namespace Core.Domain
|
||||
public int ComboMultiplier { get; private set; } = 1;
|
||||
public event Action<int> OnComboUpdated;
|
||||
|
||||
public GameSession(List<Tile> tiles, IPersistenceService persistenceService)
|
||||
public GameSession(IReadOnlyList<Tile> tiles, IPersistenceService persistenceService)
|
||||
{
|
||||
_tiles = tiles;
|
||||
_persistenceService = persistenceService;
|
||||
@@ -74,7 +74,7 @@ namespace Core.Domain
|
||||
{
|
||||
_npcTimer = 0f;
|
||||
OnSpawnNpc?.Invoke();
|
||||
NpcSpawnTime = Mathf.Max(5f, NpcSpawnTime * 0.95f);
|
||||
NpcSpawnTime = Math.Max(5f, NpcSpawnTime * 0.95f);
|
||||
}
|
||||
|
||||
_powerUpTimer += deltaTime;
|
||||
@@ -124,15 +124,12 @@ namespace Core.Domain
|
||||
|
||||
private void SpawnNextOrb()
|
||||
{
|
||||
var validTiles = _tiles.FindAll(t =>
|
||||
var validTiles = _tiles.Where(t =>
|
||||
t.CurrentState == TileState.Stable &&
|
||||
t.Floor == _playerFloorIndex
|
||||
);
|
||||
t.Floor == _playerFloorIndex).ToList();
|
||||
|
||||
if (validTiles.Count == 0)
|
||||
{
|
||||
validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable);
|
||||
}
|
||||
validTiles = _tiles.Where(t => t.CurrentState == TileState.Stable).ToList();
|
||||
|
||||
if (validTiles.Count == 0)
|
||||
{
|
||||
@@ -173,27 +170,24 @@ namespace Core.Domain
|
||||
|
||||
private void SpawnRandomPowerUp()
|
||||
{
|
||||
var validTiles = _tiles.FindAll(t =>
|
||||
t.CurrentState == TileState.Stable &&
|
||||
t.Floor == _playerFloorIndex
|
||||
);
|
||||
var validTiles = _tiles.Where(t =>
|
||||
t.CurrentState == TileState.Stable &&
|
||||
t.Floor == _playerFloorIndex).ToList();
|
||||
|
||||
if (validTiles.Count == 0)
|
||||
{
|
||||
validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable);
|
||||
}
|
||||
validTiles = _tiles.Where(t => t.CurrentState == TileState.Stable).ToList();
|
||||
|
||||
if (validTiles.Count == 0) return;
|
||||
|
||||
var tile = validTiles[_rng.Next(validTiles.Count)];
|
||||
|
||||
var rand = _rng.Next(0, 4);
|
||||
var type = PowerUpType.LightFooted;
|
||||
var rand = _rng.Next(0, 3);
|
||||
PowerUpType type;
|
||||
switch (rand)
|
||||
{
|
||||
case 0: type = PowerUpType.LightFooted; break;
|
||||
case 1: type = PowerUpType.SpeedBoost; break;
|
||||
case 3: type = PowerUpType.TimeSlow; break;
|
||||
default: type = PowerUpType.TimeSlow; break;
|
||||
}
|
||||
|
||||
OnSpawnPowerUp?.Invoke(type, tile.Id);
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Core.Domain.Status.Effects
|
||||
public void ModifyCapabilities(ref PlayerCapabilities caps)
|
||||
{
|
||||
caps.CanTriggerDecay = false;
|
||||
caps.SpeedMultiplier = 1.2f;
|
||||
caps.SpeedMultiplier *= 1.2f;
|
||||
}
|
||||
|
||||
public void OnApply()
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Core.Domain.Status.Effects
|
||||
|
||||
public void ModifyCapabilities(ref PlayerCapabilities caps)
|
||||
{
|
||||
caps.SpeedMultiplier = _multiplier;
|
||||
caps.SpeedMultiplier *= _multiplier;
|
||||
}
|
||||
|
||||
public void OnApply() { }
|
||||
|
||||
@@ -9,5 +9,6 @@ namespace Core.Ports
|
||||
void SetVisualState(TileState state);
|
||||
void DropPhysics();
|
||||
void Dispose();
|
||||
void StepOn();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Core.Domain;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Infrastructure.Unity
|
||||
@@ -10,44 +9,13 @@ namespace Infrastructure.Unity
|
||||
[SerializeField] private float fadeSpeed = 5f;
|
||||
[SerializeField] private float hiddenAlpha = 0.1f;
|
||||
[SerializeField] private float visibleAlpha = 1.0f;
|
||||
|
||||
private GameSession _gameSession;
|
||||
|
||||
private List<List<TileViewAdapter>> _floors;
|
||||
private int _currentFloorIndex = -1;
|
||||
|
||||
public void Initialize(GameSession gameSession, List<Tile> allTiles, Dictionary<string, TileViewAdapter> tileViews, int totalFloors)
|
||||
|
||||
public void Initialize(TileRegistry registry, int totalFloors)
|
||||
{
|
||||
_gameSession = gameSession;
|
||||
|
||||
_floors = new List<List<TileViewAdapter>>();
|
||||
for (var i = 0; i < totalFloors; i++)
|
||||
{
|
||||
_floors.Add(new List<TileViewAdapter>());
|
||||
}
|
||||
|
||||
foreach (var tile in allTiles)
|
||||
{
|
||||
if (tileViews.TryGetValue(tile.Id, out var view))
|
||||
{
|
||||
// Safety check for array bounds
|
||||
if (tile.Floor < _floors.Count)
|
||||
{
|
||||
_floors[tile.Floor].Add(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_gameSession == null) return;
|
||||
|
||||
// Check if player changed floors
|
||||
// We read the private field _playerFloorIndex via a public getter we need to add,
|
||||
// OR we just track it locally if you updated GameSession to expose it.
|
||||
// Assuming GameSession doesn't expose it publically yet, let's rely on GameBootstrap passing it or just hack it:
|
||||
// Ideally, GameSession should emit an event 'OnFloorChanged'.
|
||||
// For now, let's assume we can get it or we passed the player reference.
|
||||
_floors = registry.GroupViewsByFloor(totalFloors);
|
||||
}
|
||||
|
||||
// Call this from GameBootstrap.Update()
|
||||
@@ -75,9 +43,9 @@ namespace Infrastructure.Unity
|
||||
tile.SetAlpha(targetAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
if (i % 2 == 0) yield return null;
|
||||
|
||||
if (i % 2 == 0) yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Core.Domain;
|
||||
using Core.Domain.Status.Effects;
|
||||
using Core.Ports;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
@@ -24,11 +23,7 @@ namespace Infrastructure.Unity
|
||||
[SerializeField] private FloorVisibilityManager floorVisibilityManager;
|
||||
|
||||
[Header("Ui")]
|
||||
[SerializeField] private TMP_Text scoreText;
|
||||
[SerializeField] private TMP_Text highScoreText;
|
||||
[SerializeField] private GameObject gameOverUi;
|
||||
[SerializeField] private GameObject pauseUi;
|
||||
[SerializeField] private GameObject startScreenUi;
|
||||
[SerializeField] private GameUiCoordinator uiCoordinator;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private float restartTime = 3f;
|
||||
@@ -36,8 +31,7 @@ namespace Infrastructure.Unity
|
||||
[Header("Power Ups")]
|
||||
[SerializeField] private PowerUpViewAdapter powerUpPrefab;
|
||||
|
||||
private readonly List<Tile> _allTiles = new();
|
||||
private readonly Dictionary<string, TileViewAdapter> _tileViews = new();
|
||||
private readonly TileRegistry _tileRegistry = new();
|
||||
private GameSession _gameSession;
|
||||
private IPersistenceService _persistenceService;
|
||||
private InputSystem_Actions _actions;
|
||||
@@ -45,11 +39,11 @@ namespace Infrastructure.Unity
|
||||
private GameObject _currentOrbInstance;
|
||||
private bool _isGameRunning;
|
||||
private int _currentPlayerFloorIndex;
|
||||
private int _currentDisplayedScore;
|
||||
private float _inputBlockTimer;
|
||||
private bool _isPaused;
|
||||
private bool _levelGenerated;
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_actions = new InputSystem_Actions();
|
||||
@@ -67,16 +61,16 @@ namespace Infrastructure.Unity
|
||||
{
|
||||
_inputBlockTimer = 0.5f;
|
||||
_persistenceService = new PlayerPrefsPersistenceAdapter();
|
||||
_gameSession = new GameSession(_allTiles, _persistenceService);
|
||||
_gameSession = new GameSession(_tileRegistry.AllTiles, _persistenceService);
|
||||
|
||||
// Set Theme based on High Score
|
||||
ThemeManager.CurrentTheme = ThemeManager.GetTheme(_gameSession.HighScore);
|
||||
|
||||
var floorsCount = levelGenerator ? levelGenerator.FloorsCount : 1;
|
||||
|
||||
var floorsCount = levelGenerator?.Definition != null ? levelGenerator.Definition.FloorCount : 1;
|
||||
|
||||
if (levelGenerator)
|
||||
{
|
||||
StartCoroutine(levelGenerator.GenerateAsync(soundManager, _allTiles, _tileViews, cameraController,
|
||||
StartCoroutine(levelGenerator.GenerateAsync(soundManager, _tileRegistry, cameraController,
|
||||
rumbleManager,
|
||||
() =>
|
||||
{
|
||||
@@ -84,23 +78,21 @@ namespace Infrastructure.Unity
|
||||
{
|
||||
floorVisibilityManager = gameObject.AddComponent<FloorVisibilityManager>();
|
||||
}
|
||||
floorVisibilityManager.Initialize(_gameSession, _allTiles, _tileViews, floorsCount);
|
||||
floorVisibilityManager.Initialize(_tileRegistry, floorsCount);
|
||||
|
||||
SpawnDeathPlane();
|
||||
SpawnPlayer();
|
||||
|
||||
if (gameOverUi) gameOverUi.SetActive(false);
|
||||
if (startScreenUi) startScreenUi.SetActive(true); // Show start screen NOW
|
||||
uiCoordinator?.ShowStartScreen();
|
||||
uiCoordinator?.UpdateHighScore(_gameSession.HighScore);
|
||||
|
||||
WireEvents();
|
||||
UpdateScoreUi(_gameSession.Score);
|
||||
|
||||
|
||||
_levelGenerated = true;
|
||||
}));
|
||||
}
|
||||
|
||||
if (gameOverUi) gameOverUi.SetActive(false);
|
||||
if (startScreenUi) startScreenUi.SetActive(true);
|
||||
uiCoordinator?.ShowStartScreen();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -134,9 +126,12 @@ namespace Infrastructure.Unity
|
||||
// Calculate current floor index based on Y height (inverse logic from Generator)
|
||||
// Note: Generator uses negative offsets: 0, -15, -30.
|
||||
// So Floor 0 is at Y=0. Floor 1 is at Y=-15.
|
||||
|
||||
var heightDist = levelGenerator.FloorHeightDistance;
|
||||
var maxFloors = levelGenerator.FloorsCount;
|
||||
|
||||
var def = levelGenerator.Definition;
|
||||
if (def == null) return;
|
||||
|
||||
var heightDist = def.FloorHeightDistance;
|
||||
var maxFloors = def.FloorCount;
|
||||
|
||||
var rawFloor = Mathf.RoundToInt(-playerY / heightDist);
|
||||
_currentPlayerFloorIndex = Mathf.Clamp(rawFloor, 0, maxFloors - 1);
|
||||
@@ -155,9 +150,10 @@ namespace Infrastructure.Unity
|
||||
// Hard Mode: Decay faster as score increases
|
||||
var decayMultiplier = 1.0f + (_gameSession.Score / 500f);
|
||||
|
||||
for (var i = _allTiles.Count - 1; i >= 0; i--)
|
||||
var allTiles = _tileRegistry.AllTiles;
|
||||
for (var i = allTiles.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_allTiles[i].Tick(dt * dilation * decayMultiplier);
|
||||
allTiles[i].Tick(dt * dilation * decayMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,14 +165,14 @@ namespace Infrastructure.Unity
|
||||
{
|
||||
Time.timeScale = 0f;
|
||||
if (soundManager) soundManager.SetPaused(true);
|
||||
if (pauseUi) pauseUi.SetActive(true);
|
||||
uiCoordinator?.ShowPauseUi();
|
||||
if (rumbleManager) rumbleManager.SetPaused(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Time.timeScale = 1f;
|
||||
if (soundManager) soundManager.SetPaused(false);
|
||||
if (pauseUi) pauseUi.SetActive(false);
|
||||
uiCoordinator?.HidePauseUi();
|
||||
if (rumbleManager) rumbleManager.SetPaused(false);
|
||||
}
|
||||
}
|
||||
@@ -185,7 +181,7 @@ namespace Infrastructure.Unity
|
||||
{
|
||||
if (_currentOrbInstance) Destroy(_currentOrbInstance);
|
||||
|
||||
if (!_tileViews.TryGetValue(tileId, out var tileView)) return;
|
||||
if (!_tileRegistry.TryGetView(tileId, out var tileView)) return;
|
||||
if (!tileView) return;
|
||||
|
||||
var spawnPos = tileView.transform.position + Vector3.up;
|
||||
@@ -204,34 +200,6 @@ namespace Infrastructure.Unity
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateScoreUi(int newScore)
|
||||
{
|
||||
if (!scoreText) return;
|
||||
|
||||
LeanTween.cancel(scoreText.gameObject);
|
||||
|
||||
scoreText.rectTransform.localScale = Vector3.one;
|
||||
LeanTween.scale(scoreText.rectTransform, Vector3.one * 1.5f, 0.5f)
|
||||
.setEasePunch();
|
||||
|
||||
LeanTween.value(scoreText.gameObject, (float val) =>
|
||||
{
|
||||
var currentVal = Mathf.RoundToInt(val);
|
||||
|
||||
var combo = _gameSession?.ComboMultiplier ?? 1;
|
||||
var comboText = combo > 1 ? $" <color=yellow>x{combo}</color>" : "";
|
||||
|
||||
scoreText.text = $"{currentVal}{comboText}";
|
||||
}, _currentDisplayedScore, newScore, 0.5f)
|
||||
.setEaseOutExpo();
|
||||
|
||||
_currentDisplayedScore = newScore;
|
||||
|
||||
if (highScoreText && _gameSession != null)
|
||||
highScoreText.text = $"BEST: {_gameSession.HighScore}";
|
||||
|
||||
}
|
||||
|
||||
private void SpawnPlayer()
|
||||
{
|
||||
var spawnPos = new Vector3(0f, 5f, 0f);
|
||||
@@ -251,10 +219,11 @@ namespace Infrastructure.Unity
|
||||
{
|
||||
if (!levelGenerator) return;
|
||||
|
||||
var lowestY = -(levelGenerator.FloorsCount * levelGenerator.FloorHeightDistance) - 5f;
|
||||
var pos = new Vector3(levelGenerator.GridSizeX / 2f, lowestY, levelGenerator.GridSizeY / 2f);
|
||||
var def = levelGenerator.Definition;
|
||||
var lowestY = -(def.FloorCount * def.FloorHeightDistance) - 5f;
|
||||
var pos = new Vector3(def.GridSizeX / 2f, lowestY, def.GridSizeY / 2f);
|
||||
var plane = Instantiate(deathPlanePrefab, pos, Quaternion.identity);
|
||||
plane.transform.localScale = new Vector3(levelGenerator.GridSizeX * 200f, 1f, levelGenerator.GridSizeY * 200f);
|
||||
plane.transform.localScale = new Vector3(def.GridSizeX * 200f, 1f, def.GridSizeY * 200f);
|
||||
|
||||
plane.OnPlayerFell += () => _gameSession.EndGame();
|
||||
}
|
||||
@@ -264,8 +233,6 @@ namespace Infrastructure.Unity
|
||||
_isGameRunning = false;
|
||||
|
||||
if (beatPulseController) beatPulseController.StopTracking();
|
||||
|
||||
if (gameOverUi) gameOverUi.SetActive(true);
|
||||
|
||||
StartCoroutine(RestartRoutine());
|
||||
}
|
||||
@@ -279,7 +246,7 @@ namespace Infrastructure.Unity
|
||||
|
||||
private void WireEvents()
|
||||
{
|
||||
_gameSession.OnScoreChanged += UpdateScoreUi;
|
||||
uiCoordinator?.Subscribe(_gameSession);
|
||||
_gameSession.OnOrbSpawned += SpawnVisualOrb;
|
||||
_gameSession.OnOrbReset += HandleOrbReset;
|
||||
_gameSession.OnGameOver += HandleGameOver;
|
||||
@@ -303,18 +270,18 @@ namespace Infrastructure.Unity
|
||||
|
||||
private void SpawnNpc()
|
||||
{
|
||||
var validTiles = _allTiles.FindAll(t => t.Floor == 0 && t.CurrentState == TileState.Stable);
|
||||
var validTiles = _tileRegistry.FindTiles(t => t.Floor == 0 && t.CurrentState == TileState.Stable);
|
||||
|
||||
if (validTiles.Count == 0)
|
||||
{
|
||||
validTiles = _allTiles.FindAll(t => t.CurrentState == TileState.Stable);
|
||||
validTiles = _tileRegistry.FindTiles(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 (!_tileRegistry.TryGetView(randomTile.Id, out var tileView)) return;
|
||||
if (!tileView) return;
|
||||
|
||||
var spawnPos = tileView.transform.position + Vector3.up * 5f;
|
||||
@@ -336,9 +303,8 @@ namespace Infrastructure.Unity
|
||||
private void StartGameSequence()
|
||||
{
|
||||
_isGameRunning = true;
|
||||
_currentDisplayedScore = 0;
|
||||
|
||||
if (startScreenUi) startScreenUi.SetActive(false);
|
||||
|
||||
uiCoordinator?.HideStartScreen();
|
||||
|
||||
if (soundManager)
|
||||
{
|
||||
@@ -359,7 +325,7 @@ namespace Infrastructure.Unity
|
||||
|
||||
private void SpawnPowerUp(PowerUpType type, string tileId)
|
||||
{
|
||||
if (!_tileViews.TryGetValue(tileId, out var tileView)) return;
|
||||
if (!_tileRegistry.TryGetView(tileId, out var tileView)) return;
|
||||
if (!tileView) return;
|
||||
|
||||
var spawnPos = tileView.transform.position + Vector3.up * 0.5f;
|
||||
@@ -367,35 +333,47 @@ namespace Infrastructure.Unity
|
||||
|
||||
instance.Configure(type);
|
||||
|
||||
instance.OnCollected += (t) =>
|
||||
instance.OnCollected += (t, dur) =>
|
||||
{
|
||||
cameraController?.Shake(0.2f, 0.15f);
|
||||
rumbleManager?.PulseMedium();
|
||||
|
||||
if (t == PowerUpType.TimeSlow)
|
||||
|
||||
switch (t)
|
||||
{
|
||||
_gameSession.ActivateTimeSlow(10f);
|
||||
case PowerUpType.TimeSlow:
|
||||
_gameSession.ActivateTimeSlow(dur);
|
||||
break;
|
||||
case PowerUpType.LightFooted:
|
||||
_playerInstance?.Status.AddEffect(new LightFootedEffect(dur));
|
||||
break;
|
||||
case PowerUpType.SpeedBoost:
|
||||
_playerInstance?.Status.AddEffect(new SpeedBoostEffect(dur));
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning($"GameBootstrap: no effect handler for PowerUpType {t}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void OnBeatMeasure()
|
||||
{
|
||||
if (_allTiles.Count == 0) return;
|
||||
var allTiles = _tileRegistry.AllTiles;
|
||||
if (allTiles.Count == 0) return;
|
||||
|
||||
var pulseCount = 25;
|
||||
|
||||
|
||||
for (var i = 0; i < pulseCount; i++)
|
||||
{
|
||||
var randIndex = Random.Range(0, _allTiles.Count);
|
||||
var tile = _allTiles[randIndex];
|
||||
|
||||
var randIndex = Random.Range(0, allTiles.Count);
|
||||
var tile = allTiles[randIndex];
|
||||
|
||||
if (tile.Floor < _currentPlayerFloorIndex) continue;
|
||||
if (tile.Floor > _currentPlayerFloorIndex + 1) continue;
|
||||
|
||||
if (tile.CurrentState != TileState.Stable) continue;
|
||||
|
||||
if (_tileViews.TryGetValue(tile.Id, out var tileView))
|
||||
|
||||
if (_tileRegistry.TryGetView(tile.Id, out var tileView))
|
||||
{
|
||||
tileView.PulseEmission(Random.Range(1.2f, 2.0f));
|
||||
}
|
||||
|
||||
86
Assets/Scripts/Infrastructure/Unity/GameUiCoordinator.cs
Normal file
86
Assets/Scripts/Infrastructure/Unity/GameUiCoordinator.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Core.Domain;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Infrastructure.Unity
|
||||
{
|
||||
public class GameUiCoordinator : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private TMP_Text scoreText;
|
||||
[SerializeField] private TMP_Text highScoreText;
|
||||
[SerializeField] private GameObject gameOverUi;
|
||||
[SerializeField] private GameObject pauseUi;
|
||||
[SerializeField] private GameObject startScreenUi;
|
||||
|
||||
private GameSession _session;
|
||||
private int _currentDisplayedScore;
|
||||
|
||||
public void Subscribe(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
session.OnScoreChanged += UpdateScore;
|
||||
session.OnGameOver += ShowGameOverUi;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_session == null) return;
|
||||
_session.OnScoreChanged -= UpdateScore;
|
||||
_session.OnGameOver -= ShowGameOverUi;
|
||||
}
|
||||
|
||||
public void ShowStartScreen()
|
||||
{
|
||||
if (gameOverUi) gameOverUi.SetActive(false);
|
||||
if (startScreenUi) startScreenUi.SetActive(true);
|
||||
}
|
||||
|
||||
public void HideStartScreen()
|
||||
{
|
||||
if (startScreenUi) startScreenUi.SetActive(false);
|
||||
}
|
||||
|
||||
public void ShowGameOverUi()
|
||||
{
|
||||
if (gameOverUi) gameOverUi.SetActive(true);
|
||||
}
|
||||
|
||||
public void ShowPauseUi()
|
||||
{
|
||||
if (pauseUi) pauseUi.SetActive(true);
|
||||
}
|
||||
|
||||
public void HidePauseUi()
|
||||
{
|
||||
if (pauseUi) pauseUi.SetActive(false);
|
||||
}
|
||||
|
||||
public void UpdateHighScore(int highScore)
|
||||
{
|
||||
if (highScoreText) highScoreText.text = $"BEST: {highScore}";
|
||||
}
|
||||
|
||||
private void UpdateScore(int newScore)
|
||||
{
|
||||
if (!scoreText) return;
|
||||
|
||||
LeanTween.cancel(scoreText.gameObject);
|
||||
scoreText.rectTransform.localScale = Vector3.one;
|
||||
LeanTween.scale(scoreText.rectTransform, Vector3.one * 1.5f, 0.5f).setEasePunch();
|
||||
|
||||
LeanTween.value(scoreText.gameObject, (float val) =>
|
||||
{
|
||||
var combo = _session?.ComboMultiplier ?? 1;
|
||||
var comboText = combo > 1 ? $" <color=yellow>x{combo}</color>" : "";
|
||||
scoreText.text = $"{Mathf.RoundToInt(val)}{comboText}";
|
||||
}, _currentDisplayedScore, newScore, 0.5f)
|
||||
.setEaseOutExpo();
|
||||
|
||||
_currentDisplayedScore = newScore;
|
||||
|
||||
if (highScoreText && _session != null)
|
||||
highScoreText.text = $"BEST: {_session.HighScore}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2d628098b45e36879d59d2c2c2bf061
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Core.Ports;
|
||||
using UnityEngine;
|
||||
using KBCore.Refs;
|
||||
|
||||
@@ -37,9 +38,9 @@ namespace Infrastructure.Unity
|
||||
|
||||
if (Physics.Raycast(transform.position, Vector3.down, out var hit, 2f, tileLayer))
|
||||
{
|
||||
if (hit.collider.TryGetComponent<TileViewAdapter>(out var tile))
|
||||
if (hit.collider.TryGetComponent<ITileView>(out var tileView))
|
||||
{
|
||||
tile.OnPlayerStep();
|
||||
tileView.StepOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
Assets/Scripts/Infrastructure/Unity/LevelDefinition.cs
Normal file
39
Assets/Scripts/Infrastructure/Unity/LevelDefinition.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Infrastructure.Unity
|
||||
{
|
||||
[CreateAssetMenu(fileName = "LevelDefinition", menuName = "Decay Grid/Level Definition")]
|
||||
public class LevelDefinition : ScriptableObject
|
||||
{
|
||||
[SerializeField] private int gridSizeX = 10;
|
||||
[SerializeField] private int gridSizeY = 10;
|
||||
[SerializeField] private float floorHeightDistance = 15f;
|
||||
[SerializeField] private FloorConfig[] floors =
|
||||
{
|
||||
new() { pattern = FloorPatternType.Square },
|
||||
new() { pattern = FloorPatternType.Donut },
|
||||
new() { pattern = FloorPatternType.Circle }
|
||||
};
|
||||
|
||||
public int GridSizeX => gridSizeX;
|
||||
public int GridSizeY => gridSizeY;
|
||||
public float FloorHeightDistance => floorHeightDistance;
|
||||
public int FloorCount => floors.Length;
|
||||
public IReadOnlyList<FloorConfig> Floors => floors;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class FloorConfig
|
||||
{
|
||||
public FloorPatternType pattern;
|
||||
}
|
||||
|
||||
public enum FloorPatternType
|
||||
{
|
||||
Square,
|
||||
Donut,
|
||||
Circle
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1e67659d937881ee99b3b9e84f91428
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Core.Domain;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Infrastructure.Unity
|
||||
@@ -16,50 +17,68 @@ namespace Infrastructure.Unity
|
||||
[SerializeField] private JumpPadAdapter jumpPadPrefab;
|
||||
[SerializeField] private TeleporterAdapter teleporterPrefab;
|
||||
|
||||
[Header("Level")]
|
||||
[SerializeField] private LevelDefinition levelDefinition;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private int gridSizeX = 10;
|
||||
[SerializeField] private int gridSizeY = 10;
|
||||
[SerializeField] private int floorsCount = 3;
|
||||
[SerializeField] private float floorHeightDistance = 15f;
|
||||
[SerializeField] private float decayTime = 0.5f;
|
||||
[SerializeField] private float fallingTime = 2.0f;
|
||||
[SerializeField] private float minimumDistanceBetweenTeleporters = 3f;
|
||||
|
||||
public float FloorHeightDistance => floorHeightDistance;
|
||||
public int FloorsCount => floorsCount;
|
||||
public int GridSizeX => gridSizeX;
|
||||
public int GridSizeY => gridSizeY;
|
||||
public LevelDefinition Definition => levelDefinition;
|
||||
|
||||
private TilePool _tilePool;
|
||||
|
||||
public IEnumerator GenerateAsync(SoundManager soundManager, List<Tile> allTiles, Dictionary<string, TileViewAdapter> tileViews, CameraController camera, RumbleManager rumble, Action onComplete)
|
||||
public IEnumerator GenerateAsync(SoundManager soundManager, TileRegistry registry, CameraController camera, RumbleManager rumble, Action onComplete)
|
||||
{
|
||||
if (!levelDefinition)
|
||||
{
|
||||
Debug.LogError("LevelGenerator: levelDefinition is not assigned. Assign a LevelDefinition asset in the Inspector.");
|
||||
onComplete?.Invoke();
|
||||
yield break;
|
||||
}
|
||||
|
||||
_tilePool = new TilePool(tilePrefab, transform);
|
||||
|
||||
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
yield return GenerateFloorAsync(0, MapPatterns.GenerateSquare(gridSizeX, gridSizeY), soundManager, allTiles, tileViews, camera, rumble, stopwatch);
|
||||
yield return GenerateFloorAsync(1, MapPatterns.GenerateDonut(gridSizeX, Mathf.FloorToInt(gridSizeX / 3f)), soundManager, allTiles, tileViews, camera, rumble, stopwatch);
|
||||
yield return GenerateFloorAsync(2, MapPatterns.GenerateCircle(gridSizeX), soundManager, allTiles, tileViews, camera, rumble, stopwatch);
|
||||
|
||||
stopwatch?.Stop();
|
||||
for (var i = 0; i < levelDefinition.FloorCount; i++)
|
||||
{
|
||||
var coords = GetCoordsForFloor(levelDefinition.Floors[i].pattern);
|
||||
yield return GenerateFloorAsync(i, coords, soundManager, registry, camera, rumble, stopwatch);
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
|
||||
private IEnumerator GenerateFloorAsync(int floorIndex, List<Vector2Int> coordinates, SoundManager soundManager,
|
||||
List<Tile> allTiles, Dictionary<string, TileViewAdapter> tileViews, CameraController camera, RumbleManager rumble, Stopwatch stopwatch)
|
||||
private List<Vector2Int> GetCoordsForFloor(FloorPatternType pattern)
|
||||
{
|
||||
var yOffset = -(floorIndex * floorHeightDistance);
|
||||
var xOffset = gridSizeX / 2f;
|
||||
var zOffset = gridSizeY / 2f;
|
||||
|
||||
var sizeX = levelDefinition.GridSizeX;
|
||||
var sizeY = levelDefinition.GridSizeY;
|
||||
return pattern switch
|
||||
{
|
||||
FloorPatternType.Square => MapPatterns.GenerateSquare(sizeX, sizeY),
|
||||
FloorPatternType.Donut => MapPatterns.GenerateDonut(sizeX, Mathf.FloorToInt(sizeX / 3f)),
|
||||
FloorPatternType.Circle => MapPatterns.GenerateCircle(sizeX),
|
||||
_ => MapPatterns.GenerateSquare(sizeX, sizeY)
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerator GenerateFloorAsync(int floorIndex, List<Vector2Int> coordinates, SoundManager soundManager,
|
||||
TileRegistry registry, CameraController camera, RumbleManager rumble, Stopwatch stopwatch)
|
||||
{
|
||||
var yOffset = -(floorIndex * levelDefinition.FloorHeightDistance);
|
||||
var xOffset = levelDefinition.GridSizeX / 2f;
|
||||
var zOffset = levelDefinition.GridSizeY / 2f;
|
||||
|
||||
const long frameBudgetMs = 5;
|
||||
|
||||
foreach (var coord in coordinates)
|
||||
{
|
||||
var pos = new Vector3(coord.x - xOffset, yOffset, coord.y - zOffset);
|
||||
CreateTile(pos, $"{floorIndex}_{coord.x}_{coord.y}", floorIndex, soundManager, allTiles, tileViews, camera, rumble);
|
||||
CreateTile(pos, $"{floorIndex}_{coord.x}_{coord.y}", floorIndex, soundManager, registry, camera, rumble);
|
||||
|
||||
if (stopwatch.ElapsedMilliseconds > frameBudgetMs)
|
||||
{
|
||||
@@ -129,7 +148,7 @@ namespace Infrastructure.Unity
|
||||
}
|
||||
|
||||
private void CreateTile(Vector3 position, string id, int floorIndex, SoundManager soundManager,
|
||||
List<Tile> allTiles, Dictionary<string, TileViewAdapter> tileViews, CameraController camera, RumbleManager rumble)
|
||||
TileRegistry registry, CameraController camera, RumbleManager rumble)
|
||||
{
|
||||
var go = _tilePool.Get();
|
||||
go.transform.position = position;
|
||||
@@ -172,8 +191,7 @@ namespace Infrastructure.Unity
|
||||
}
|
||||
};
|
||||
|
||||
allTiles.Add(tileLogic);
|
||||
tileViews.Add(id, go);
|
||||
registry.Register(tileLogic, go);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Core.Ports;
|
||||
using KBCore.Refs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
@@ -49,9 +50,9 @@ namespace Infrastructure.Unity
|
||||
|
||||
if (Physics.Raycast(transform.position, Vector3.down, out var hit, 2.0f, tileLayer))
|
||||
{
|
||||
if (hit.collider.TryGetComponent<TileViewAdapter>(out var tile))
|
||||
if (hit.collider.TryGetComponent<ITileView>(out var tileView))
|
||||
{
|
||||
tile.OnPlayerStep();
|
||||
tileView.StepOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Core.Domain.Status;
|
||||
using Core.Domain.Status.Effects;
|
||||
using Core.Ports;
|
||||
using KBCore.Refs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
@@ -244,9 +245,9 @@ namespace Infrastructure.Unity
|
||||
{
|
||||
if (Physics.SphereCast(transform.position, 0.3f, Vector3.down, out var hit, groundCheckDistance, tileLayer))
|
||||
{
|
||||
if (hit.collider.TryGetComponent<TileViewAdapter>(out var tileAdapter))
|
||||
if (hit.collider.TryGetComponent<ITileView>(out var tileView))
|
||||
{
|
||||
tileAdapter.OnPlayerStep();
|
||||
tileView.StepOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Infrastructure.Unity
|
||||
private MaterialPropertyBlock _propBlock;
|
||||
private static readonly int ColorProperty = Shader.PropertyToID("_BaseColor");
|
||||
|
||||
public event Action<PowerUpType> OnCollected;
|
||||
public event Action<PowerUpType, float> OnCollected;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -45,20 +45,16 @@ namespace Infrastructure.Unity
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (other.TryGetComponent<PlayerController>(out var player))
|
||||
if (other.TryGetComponent<PlayerController>(out _))
|
||||
{
|
||||
ApplyEffect(player);
|
||||
OnCollected?.Invoke(type);
|
||||
OnCollected?.Invoke(type, duration);
|
||||
|
||||
if (pickupVfx)
|
||||
{
|
||||
var vfx = Instantiate(pickupVfx, transform.position, Quaternion.identity);
|
||||
var main = vfx.main;
|
||||
|
||||
meshRenderer.GetPropertyBlock(_propBlock);
|
||||
var currentColor = _propBlock.GetColor(ColorProperty);
|
||||
main.startColor = currentColor;
|
||||
|
||||
main.startColor = _propBlock.GetColor(ColorProperty);
|
||||
Destroy(vfx.gameObject, 2f);
|
||||
}
|
||||
|
||||
@@ -66,24 +62,6 @@ namespace Infrastructure.Unity
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyEffect(PlayerController player)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PowerUpType.LightFooted:
|
||||
player.Status.AddEffect(new LightFootedEffect(duration));
|
||||
break;
|
||||
case PowerUpType.SpeedBoost:
|
||||
player.Status.AddEffect(new SpeedBoostEffect(duration, 1.5f));
|
||||
break;
|
||||
case PowerUpType.TimeSlow:
|
||||
// Handled globally
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(PowerUpType newType)
|
||||
{
|
||||
type = newType;
|
||||
|
||||
46
Assets/Scripts/Infrastructure/Unity/TileRegistry.cs
Normal file
46
Assets/Scripts/Infrastructure/Unity/TileRegistry.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Core.Domain;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Infrastructure.Unity
|
||||
{
|
||||
public class TileRegistry
|
||||
{
|
||||
private readonly List<Tile> _tiles = new();
|
||||
private readonly Dictionary<string, TileViewAdapter> _views = new();
|
||||
|
||||
public IReadOnlyList<Tile> AllTiles => _tiles;
|
||||
|
||||
public void Register(Tile tile, TileViewAdapter view = null)
|
||||
{
|
||||
_tiles.Add(tile);
|
||||
if (view != null) _views[tile.Id] = view;
|
||||
}
|
||||
|
||||
public bool TryGetView(string tileId, out TileViewAdapter view)
|
||||
=> _views.TryGetValue(tileId, out view);
|
||||
|
||||
public List<Tile> FindTiles(Predicate<Tile> predicate)
|
||||
=> _tiles.FindAll(predicate);
|
||||
|
||||
public List<List<TileViewAdapter>> GroupViewsByFloor(int floorCount)
|
||||
{
|
||||
var floors = new List<List<TileViewAdapter>>(floorCount);
|
||||
for (var i = 0; i < floorCount; i++) floors.Add(new List<TileViewAdapter>());
|
||||
|
||||
foreach (var tile in _tiles)
|
||||
{
|
||||
if (tile.Floor >= floorCount)
|
||||
{
|
||||
Debug.LogWarning($"TileRegistry: tile '{tile.Id}' floor {tile.Floor} >= floorCount {floorCount}, skipping");
|
||||
continue;
|
||||
}
|
||||
if (_views.TryGetValue(tile.Id, out var view))
|
||||
floors[tile.Floor].Add(view);
|
||||
}
|
||||
|
||||
return floors;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Infrastructure/Unity/TileRegistry.cs.meta
Normal file
2
Assets/Scripts/Infrastructure/Unity/TileRegistry.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9e18fc22049e4e8c97b14af34cc4747
|
||||
@@ -106,7 +106,7 @@ namespace Infrastructure.Unity
|
||||
StartCoroutine(ShrinkAndDestroy());
|
||||
}
|
||||
|
||||
public void OnPlayerStep()
|
||||
public void StepOn()
|
||||
{
|
||||
_linkedTile?.StepOn();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ MonoBehaviour:
|
||||
m_StripUnusedVariants: 1
|
||||
m_StripScreenCoordOverrideVariants: 1
|
||||
supportRuntimeDebugDisplay: 0
|
||||
m_EnableRenderGraph: 0
|
||||
m_Settings:
|
||||
m_SettingsList:
|
||||
m_List:
|
||||
@@ -68,7 +69,23 @@ MonoBehaviour:
|
||||
- rid: 2396665312000868355
|
||||
- rid: 2396665312000868356
|
||||
m_RuntimeSettings:
|
||||
m_List: []
|
||||
m_List:
|
||||
- rid: 6852985685364965378
|
||||
- rid: 6852985685364965379
|
||||
- rid: 6852985685364965380
|
||||
- rid: 6852985685364965381
|
||||
- rid: 6852985685364965384
|
||||
- rid: 6852985685364965385
|
||||
- rid: 6852985685364965392
|
||||
- rid: 6852985685364965394
|
||||
- rid: 8712630790384254976
|
||||
- rid: 3311242227245645828
|
||||
- rid: 3311242227245645829
|
||||
- rid: 3311242227245645830
|
||||
- rid: 3311242227245645832
|
||||
- rid: 3311242227245645834
|
||||
- rid: 3311242227245645835
|
||||
- rid: 2396665312000868354
|
||||
m_AssetVersion: 10
|
||||
m_ObsoleteDefaultVolumeProfile: {fileID: 0}
|
||||
m_RenderingLayerNames:
|
||||
@@ -99,6 +116,8 @@ MonoBehaviour:
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: -2
|
||||
type: {class: , ns: , asm: }
|
||||
- rid: 2396665312000868354
|
||||
type: {class: UniversalRenderPipelineRuntimeTerrainShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
||||
data:
|
||||
@@ -362,6 +381,7 @@ MonoBehaviour:
|
||||
type: {class: RenderGraphSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
||||
data:
|
||||
m_Version: 0
|
||||
m_EnableRenderCompatibilityMode: 0
|
||||
- rid: 6852985685364965386
|
||||
type: {class: GPUResidentDrawerResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.GPUDriven.Runtime}
|
||||
data:
|
||||
|
||||
8
Assets/Tests.meta
Normal file
8
Assets/Tests.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0980498940170826f97da8599b70e474
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Tests/EditMode.meta
Normal file
8
Assets/Tests/EditMode.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84756af79e10fce108b1a3dd15cbe7ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
44
Assets/Tests/EditMode/StatusManagerTests.cs
Normal file
44
Assets/Tests/EditMode/StatusManagerTests.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using NUnit.Framework;
|
||||
using Core.Domain.Status;
|
||||
using Core.Domain.Status.Effects;
|
||||
|
||||
namespace DecayGrid.Tests
|
||||
{
|
||||
public class StatusManagerTests
|
||||
{
|
||||
[Test]
|
||||
public void DefaultCapabilities_HaveExpectedValues()
|
||||
{
|
||||
var sm = new StatusManager();
|
||||
var caps = sm.CurrentCapabilities;
|
||||
Assert.IsTrue(caps.CanTriggerDecay);
|
||||
Assert.AreEqual(1f, caps.SpeedMultiplier, 0.001f);
|
||||
Assert.IsFalse(caps.CanHover);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpeedBoostAlone_AppliesMultiplier()
|
||||
{
|
||||
var sm = new StatusManager();
|
||||
sm.AddEffect(new SpeedBoostEffect(10f, 1.5f));
|
||||
Assert.AreEqual(1.5f, sm.CurrentCapabilities.SpeedMultiplier, 0.001f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LightFootedAlone_AppliesMultiplier()
|
||||
{
|
||||
var sm = new StatusManager();
|
||||
sm.AddEffect(new LightFootedEffect(10f));
|
||||
Assert.AreEqual(1.2f, sm.CurrentCapabilities.SpeedMultiplier, 0.001f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TwoSpeedEffectsStack_Multiplicatively()
|
||||
{
|
||||
var sm = new StatusManager();
|
||||
sm.AddEffect(new LightFootedEffect(10f)); // *1.2
|
||||
sm.AddEffect(new SpeedBoostEffect(10f, 1.5f)); // *1.5
|
||||
Assert.AreEqual(1.8f, sm.CurrentCapabilities.SpeedMultiplier, 0.001f); // 1.0 * 1.2 * 1.5
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Tests/EditMode/StatusManagerTests.cs.meta
Normal file
2
Assets/Tests/EditMode/StatusManagerTests.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14aaf27a4073981cea2d26cc74361e3d
|
||||
69
Assets/Tests/EditMode/TileRegistryTests.cs
Normal file
69
Assets/Tests/EditMode/TileRegistryTests.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using NUnit.Framework;
|
||||
using Core.Domain;
|
||||
using Infrastructure.Unity;
|
||||
|
||||
namespace DecayGrid.Tests
|
||||
{
|
||||
public class TileRegistryTests
|
||||
{
|
||||
private TileRegistry _registry;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => _registry = new TileRegistry();
|
||||
|
||||
[Test]
|
||||
public void Register_AddsToAllTiles()
|
||||
{
|
||||
var tile = new Tile("0_0_0", 0, 0.5f, 2f);
|
||||
_registry.Register(tile);
|
||||
Assert.AreEqual(1, _registry.AllTiles.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindTiles_ReturnsMatchingTiles()
|
||||
{
|
||||
var tile = new Tile("0_0_0", 0, 0.5f, 2f);
|
||||
_registry.Register(tile);
|
||||
var result = _registry.FindTiles(t => t.CurrentState == TileState.Stable);
|
||||
Assert.AreEqual(1, result.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindTiles_ExcludesNonMatchingTiles()
|
||||
{
|
||||
var tile = new Tile("0_0_0", 0, 0.5f, 2f);
|
||||
tile.StepOn(); // Now Warning
|
||||
_registry.Register(tile);
|
||||
var result = _registry.FindTiles(t => t.CurrentState == TileState.Stable);
|
||||
Assert.AreEqual(0, result.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetView_ReturnsFalse_WhenTileHasNoView()
|
||||
{
|
||||
var tile = new Tile("0_0_0", 0, 0.5f, 2f);
|
||||
_registry.Register(tile); // no view
|
||||
Assert.IsFalse(_registry.TryGetView("0_0_0", out _));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GroupViewsByFloor_PlacesTileOnCorrectFloor()
|
||||
{
|
||||
// Can't create TileViewAdapter in EditMode (MonoBehaviour), so just verify tile with no view doesn't crash
|
||||
var tile = new Tile("1_5_5", 1, 0.5f, 2f);
|
||||
_registry.Register(tile); // no view
|
||||
var floors = _registry.GroupViewsByFloor(3);
|
||||
Assert.AreEqual(3, floors.Count);
|
||||
// Tile has no view registered, so no view should appear in any floor
|
||||
Assert.AreEqual(0, floors[1].Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GroupViewsByFloor_OutOfRangeTile_DoesNotThrow()
|
||||
{
|
||||
var tile = new Tile("5_0_0", 5, 0.5f, 2f); // floor 5, but only 3 floors
|
||||
_registry.Register(tile);
|
||||
Assert.DoesNotThrow(() => _registry.GroupViewsByFloor(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Tests/EditMode/TileRegistryTests.cs.meta
Normal file
2
Assets/Tests/EditMode/TileRegistryTests.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d6a64903e18ff18094c2db56bf61f38
|
||||
@@ -8,8 +8,10 @@
|
||||
"com.unity.inputsystem": "1.19.0",
|
||||
"com.unity.multiplayer.center": "1.0.1",
|
||||
"com.unity.render-pipelines.universal": "17.4.0",
|
||||
"com.unity.sdk.linux-x86_64": "1.1.0",
|
||||
"com.unity.test-framework": "1.6.0",
|
||||
"com.unity.timeline": "1.8.12",
|
||||
"com.unity.toolchain.linux-x86_64-linux": "1.1.0",
|
||||
"com.unity.ugui": "2.0.0",
|
||||
"com.unity.visualscripting": "1.9.11",
|
||||
"com.unity.modules.accessibility": "1.0.0",
|
||||
|
||||
@@ -34,16 +34,17 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.collections": {
|
||||
"version": "6.4.0",
|
||||
"version": "2.6.5",
|
||||
"depth": 2,
|
||||
"source": "builtin",
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.burst": "1.8.23",
|
||||
"com.unity.burst": "1.8.27",
|
||||
"com.unity.mathematics": "1.3.2",
|
||||
"com.unity.nuget.mono-cecil": "1.11.5",
|
||||
"com.unity.test-framework": "1.4.6",
|
||||
"com.unity.nuget.mono-cecil": "1.11.6",
|
||||
"com.unity.test-framework.performance": "3.0.3"
|
||||
}
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ext.nunit": {
|
||||
"version": "2.0.5",
|
||||
@@ -101,7 +102,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.render-pipelines.core": {
|
||||
"version": "17.4.0",
|
||||
"version": "17.3.0",
|
||||
"depth": 1,
|
||||
"source": "builtin",
|
||||
"dependencies": {
|
||||
@@ -109,28 +110,38 @@
|
||||
"com.unity.mathematics": "1.3.2",
|
||||
"com.unity.ugui": "2.0.0",
|
||||
"com.unity.collections": "2.4.3",
|
||||
"com.unity.modules.physics": "1.0.0",
|
||||
"com.unity.modules.terrain": "1.0.0",
|
||||
"com.unity.modules.jsonserialize": "1.0.0"
|
||||
}
|
||||
},
|
||||
"com.unity.render-pipelines.universal": {
|
||||
"version": "17.4.0",
|
||||
"version": "17.3.0",
|
||||
"depth": 0,
|
||||
"source": "builtin",
|
||||
"dependencies": {
|
||||
"com.unity.render-pipelines.core": "17.4.0",
|
||||
"com.unity.shadergraph": "17.4.0",
|
||||
"com.unity.render-pipelines.universal-config": "17.4.0"
|
||||
"com.unity.render-pipelines.core": "17.3.0",
|
||||
"com.unity.shadergraph": "17.3.0",
|
||||
"com.unity.render-pipelines.universal-config": "17.0.3"
|
||||
}
|
||||
},
|
||||
"com.unity.render-pipelines.universal-config": {
|
||||
"version": "17.4.0",
|
||||
"version": "17.0.3",
|
||||
"depth": 1,
|
||||
"source": "builtin",
|
||||
"dependencies": {
|
||||
"com.unity.render-pipelines.core": "17.4.0"
|
||||
"com.unity.render-pipelines.core": "17.0.3"
|
||||
}
|
||||
},
|
||||
"com.unity.sdk.linux-x86_64": {
|
||||
"version": "1.1.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.sysroot.base": "1.1.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.searcher": {
|
||||
"version": "4.9.4",
|
||||
"depth": 2,
|
||||
@@ -139,14 +150,21 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.shadergraph": {
|
||||
"version": "17.4.0",
|
||||
"version": "17.3.0",
|
||||
"depth": 1,
|
||||
"source": "builtin",
|
||||
"dependencies": {
|
||||
"com.unity.render-pipelines.core": "17.4.0",
|
||||
"com.unity.render-pipelines.core": "17.3.0",
|
||||
"com.unity.searcher": "4.9.3"
|
||||
}
|
||||
},
|
||||
"com.unity.sysroot.base": {
|
||||
"version": "1.1.0",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.test-framework": {
|
||||
"version": "1.6.0",
|
||||
"depth": 0,
|
||||
@@ -179,6 +197,15 @@
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.toolchain.linux-x86_64-linux": {
|
||||
"version": "1.1.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.sysroot.base": "1.1.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ugui": {
|
||||
"version": "2.0.0",
|
||||
"depth": 0,
|
||||
|
||||
@@ -114,6 +114,7 @@ PlayerSettings:
|
||||
xboxEnableGuest: 0
|
||||
xboxEnablePIXSampling: 0
|
||||
metalFramebufferOnly: 0
|
||||
metalUseMetalDisplayLink: 0
|
||||
xboxOneResolution: 0
|
||||
xboxOneSResolution: 0
|
||||
xboxOneXResolution: 3
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
m_EditorVersion: 6000.4.3f1
|
||||
m_EditorVersionWithRevision: 6000.4.3f1 (39d1a88d4dd1)
|
||||
m_EditorVersion: 6000.3.15f1
|
||||
m_EditorVersionWithRevision: 6000.3.15f1 (c1aa84e375f6)
|
||||
|
||||
1327
docs/superpowers/plans/2026-05-14-architecture-refactor.md
Normal file
1327
docs/superpowers/plans/2026-05-14-architecture-refactor.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user