using System.Collections.Generic;
using UnityEngine;
using Zenject;
namespace DanroJump.Gameplay
{
///
/// Выбирает активный этап сложности по максимальной высоте игрока или Y-координате генерации.
///
public sealed class GameplayDifficultyProgression : MonoBehaviour, IGameplayDifficultyProgression
{
[SerializeField] private JumpPlayerController player;
[SerializeField] private bool progressionEnabled = true;
[SerializeField] private List stages = new();
///
/// Показывает, есть ли хотя бы один настроенный этап сложности.
///
public bool HasStages => progressionEnabled && stages != null && stages.Exists(stage => stage != null);
///
/// Начальная Y-координата генерации по первому этапу.
///
public float InitialSpawnY => GetInitialSpawnY();
///
/// Прогресс игрока внутри текущего этапа по максимальной высоте забега.
///
public float CurrentProgress => CalculateProgress(player != null ? player.HighestY : 0f);
///
/// Имя текущего этапа игрока для HUD и editor overlay.
///
public string CurrentStageName => GetStage(player != null ? player.HighestY : 0f)?.StageName ?? string.Empty;
///
/// Получает игрока из SceneContext, чтобы UI и диагностические окна видели текущий этап забега.
///
[Inject]
public void Construct(JumpPlayerController injectedPlayer)
{
player = injectedPlayer;
}
///
/// Выбирает prefab платформы для указанной высоты по настройкам активного этапа.
///
public GameObject GetPlatformPrefab(float worldY, System.Random random)
{
var stage = GetStage(worldY);
return stage != null ? stage.GetPlatformPrefab(random) : null;
}
///
/// Возвращает расстояние между платформами для указанной высоты.
///
public float GetVerticalSpacing(float worldY)
{
var stage = GetStage(worldY);
if (stage == null)
{
return 0f;
}
return stage.GetVerticalSpacing(worldY);
}
///
/// Возвращает распределение платформ по ширине уровня для указанной высоты.
///
public float GetHorizontalDistribution(float worldY)
{
var stage = GetStage(worldY);
return stage != null ? stage.GetHorizontalDistribution() : 0f;
}
///
/// Создает профиль контента активного этапа: collectibles, equipment и enemies.
///
public IPlatformContentSource CreateContentSource(float worldY)
{
var stage = GetStage(worldY);
if (stage == null)
{
return null;
}
return stage.CreateContentSource(worldY);
}
///
/// Пробует выбрать процедурного врага для указанной высоты из активного ScriptableObject-этапа.
///
public bool TryGetProceduralEnemyPrefab(float worldY, System.Random random, out GameObject prefab)
{
var stage = GetStage(worldY);
if (stage == null)
{
prefab = null;
return false;
}
return stage.TryGetProceduralEnemyPrefab(worldY, random, out prefab);
}
///
/// Возвращает имя этапа для диагностики и overlay-окна.
///
public string GetStageName(float worldY)
{
return GetStage(worldY)?.StageName ?? string.Empty;
}
///
/// Возвращает прогресс внутри текущего этапа в диапазоне 0..1.
///
public float GetStageProgress(float worldY)
{
var stage = GetStage(worldY);
return stage != null ? stage.CalculateProgress(worldY) : 0f;
}
private float CalculateProgress(float worldY)
{
var stage = GetStage(worldY);
return stage != null ? stage.CalculateProgress(worldY) : 0f;
}
///
/// Возвращает стартовую высоту генерации из первого доступного этапа.
///
private float GetInitialSpawnY()
{
var firstStage = GetFirstStage();
return firstStage != null ? firstStage.StartY : 0f;
}
///
/// Находит первый этап по минимальной стартовой высоте.
///
private GameplayDifficultyStage GetFirstStage()
{
if (stages == null || stages.Count == 0)
{
return null;
}
GameplayDifficultyStage firstStage = null;
foreach (var stage in stages)
{
if (stage == null)
{
continue;
}
if (firstStage == null || stage.StartY < firstStage.StartY)
{
firstStage = stage;
}
}
return firstStage;
}
///
/// Находит последний этап, стартовая высота которого уже пройдена.
///
private GameplayDifficultyStage GetStage(float worldY)
{
if (!progressionEnabled || stages == null || stages.Count == 0)
{
return null;
}
GameplayDifficultyStage currentStage = null;
foreach (var stage in stages)
{
if (stage == null || worldY < stage.StartY)
{
continue;
}
if (currentStage == null || stage.StartY >= currentStage.StartY)
{
currentStage = stage;
}
}
// Последний достигнутый этап намеренно продолжается бесконечно.
// verticalLength управляет только прогрессом кривых, а не выключением этапа.
return currentStage;
}
}
///
/// Интерфейс чтения прогрессии сложности для gameplay-систем и диагностических окон.
///
public interface IGameplayDifficultyProgression
{
float CurrentProgress { get; }
string CurrentStageName { get; }
bool HasStages { get; }
float InitialSpawnY { get; }
GameObject GetPlatformPrefab(float worldY, System.Random random);
float GetVerticalSpacing(float worldY);
float GetHorizontalDistribution(float worldY);
IPlatformContentSource CreateContentSource(float worldY);
bool TryGetProceduralEnemyPrefab(float worldY, System.Random random, out GameObject prefab);
string GetStageName(float worldY);
float GetStageProgress(float worldY);
}
}