431 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Cysharp.Threading.Tasks;
using System;
using System.Threading;
using UnityEngine;
namespace Learn
{
public class LearnSystem : MonoBehaviour
{
[Header("Настройки подсказок")]
[SerializeField] private Tip shootTip;
[SerializeField] private Tip diveTip;
[SerializeField] private Tip weaponTip;
[SerializeField] private Tip ammoTip;
[Header("Настройки обучения")]
[SerializeField] private float shootDelay = 5f; // Время без стрельбы
[SerializeField] private float diveDepthPercent = 0.3f; // Глубина погружения для уворота
[SerializeField] private float ammoDelay = 3f; // Задержка перед подсказкой о патронах
[Header("Компоненты")]
[SerializeField] private TipView view;
[SerializeField] private GameObject player; // Ссылка на игрока
// Состояния обучения
private bool isShootLearned = false;
private bool isDiveLearned = false;
private bool isWeaponLearned = false;
private bool isAmmoLearned = false;
// Отмена задач
private CancellationTokenSource shootCts;
private CancellationTokenSource diveCts;
private CancellationTokenSource weaponCts;
private CancellationTokenSource ammoCts;
private CancellationTokenSource viewCts;
// Таймеры для отложенных проверок
private float lastShootTime;
private float weaponEmptyTime;
private float ammoEmptyTime;
// Флаги состояний
private bool isDiveLearningActive = false;
private bool isWeaponLearningActive = false;
private bool isAmmoLearningActive = false;
private bool isWeaponSwitchRequested = false;
public void Init()
{
view.Hide();
if (!SettingsManager.GetBool(SettingKey.Learn)) return;
// Подписываемся на события (замените G.WeaponModel на ваши реальные системы)
SubscribeToEvents();
// Запускаем все системы обучения
StartShootLearning();
StartDiveLearning();
StartWeaponLearning();
StartAmmoLearning();
}
#region Подписка на события
private void Update()
{
if (!isDiveLearned)
OnPlayerDive(G.PlayerStateModel.PlayerComponent.DiveSystem.DepthPercent);
}
private void SubscribeToEvents()
{
// ЗДЕСЬ ПОДПИШИТЕСЬ НА ВАШИ СОБЫТИЯ
// Примеры:
// PlayerController.OnWeaponSwitch += OnWeaponSwitch;
// PlayerController.OnAmmoRequest += OnAmmoRequest;
// PlayerController.OnWeaponEmpty += OnWeaponEmpty;
// PlayerController.OnAmmoEmpty += OnAmmoEmpty;
G.PlayerStateModel.PlayerHealthComponent.Damaged += OnProjectileHitPlayer;
// PlayerController.OnProjectileHit += OnProjectileHit;
G.DangersModel.OnProjectileSpawned += OnFirstShoot;
G.WeaponModel.OnPlayerShoot += OnPlayerShoot;
G.WeaponModel.OnWeaponChanged += OnWeaponSwitch;
G.WeaponModel.OnCurrentWeaponEmpty += OnCurrentWeaponEmpty;
G.WeaponModel.OnNoAmmo += OnAmmoEmpty;
AirDropManager.AmmoRequested += OnAmmoRequest;
}
private void UnsubscribeFromEvents()
{
G.WeaponModel.OnPlayerShoot -= OnPlayerShoot;
G.PlayerStateModel.PlayerHealthComponent.Damaged -= OnProjectileHitPlayer;
G.DangersModel.OnProjectileSpawned -= OnFirstShoot;
G.WeaponModel.OnWeaponChanged -= OnWeaponSwitch;
G.WeaponModel.OnCurrentWeaponEmpty -= OnCurrentWeaponEmpty;
G.WeaponModel.OnNoAmmo -= OnAmmoEmpty;
AirDropManager.AmmoRequested -= OnAmmoRequest;
}
#endregion
#region 1. Обучение стрельбе
private void StartShootLearning()
{
shootCts?.Cancel();
shootCts?.Dispose();
shootCts = new CancellationTokenSource();
lastShootTime = Time.time;
ShootLearnLoop(shootCts.Token).Forget();
}
public void OnPlayerShoot()
{
lastShootTime = Time.time;
// Если обучение стрельбе активно - прерываем
if (!isShootLearned)
{
StopCurrentView();
isShootLearned = true;
shootCts?.Cancel();
Debug.Log("[Learn] Обучение стрельбе завершено!");
}
}
private async UniTask ShootLearnLoop(CancellationToken ct)
{
while (!ct.IsCancellationRequested && !isShootLearned)
{
// Ждем указанное время без выстрела
await UniTask.Delay(TimeSpan.FromSeconds(shootDelay), cancellationToken: ct);
if (!isShootLearned && !ct.IsCancellationRequested)
{
// Показываем подсказку
await ShowTip(shootTip, ct);
// Ждем пока игрок не выстрелит
while (!isShootLearned && !ct.IsCancellationRequested)
{
await UniTask.Yield(ct);
}
// Скрываем подсказку после выстрела
await HideTip(ct);
}
}
}
#endregion
#region 2. Обучение нырянию
private void StartDiveLearning()
{
diveCts?.Cancel();
diveCts?.Dispose();
diveCts = new CancellationTokenSource();
}
// Вызывается при первом выстреле ИЛИ при попадании снаряда
public void OnFirstShoot(AbstractProjectile projectile)
{
if (isDiveLearned) return;
if(projectile.Owner == player) return;
// Начинаем процесс обучения нырянию
isDiveLearningActive = true;
DiveLearnProcess(diveCts.Token).Forget();
}
public void OnProjectileHitPlayer(float damage)
{
if (isDiveLearned || !isDiveLearningActive) return;
// Снаряд попал в игрока - показываем подсказку
StopCurrentView();
ShowTip(diveTip, diveCts.Token).Forget();
}
public void OnPlayerDive(float depthPercent)
{
if (isDiveLearned || !isDiveLearningActive) return;
// Проверяем глубину погружения
if (depthPercent >= diveDepthPercent)
{
// Игрок нырнул на нужную глубину - обучение завершено
isDiveLearningActive = false;
isDiveLearned = true;
diveCts?.Cancel();
StopCurrentView();
Debug.Log($"[Learn] Обучение нырянию завершено! Глубина: {depthPercent:P0}");
}
}
private async UniTask DiveLearnProcess(CancellationToken ct)
{
// Ждем пока активен процесс обучения
while (isDiveLearningActive && !ct.IsCancellationRequested)
{
await UniTask.Yield(ct);
}
// Скрываем подсказку если она висит
await HideTip(ct);
}
#endregion
#region 3. Обучение смене оружия
private void StartWeaponLearning()
{
weaponCts?.Cancel();
weaponCts?.Dispose();
weaponCts = new CancellationTokenSource();
isWeaponLearningActive = true;
WeaponLearnLoop(weaponCts.Token).Forget();
}
public void OnCurrentWeaponEmpty()
{
if (isWeaponLearned) return;
weaponEmptyTime = Time.time;
isWeaponSwitchRequested = true;
}
public void OnWeaponSwitch(Weapon _)
{
if (isWeaponLearned) return;
// Игрок сам поменял оружие - прерываем обучение
isWeaponLearningActive = false;
isWeaponLearned = true;
isWeaponSwitchRequested = false;
weaponCts?.Cancel();
StopCurrentView();
Debug.Log("[Learn] Обучение смене оружия завершено!");
}
private async UniTask WeaponLearnLoop(CancellationToken ct)
{
while (isWeaponLearningActive && !ct.IsCancellationRequested)
{
// Проверяем: оружие пустое и прошло 3 секунды
if (isWeaponSwitchRequested && Time.time - weaponEmptyTime >= 3f)
{
// Показываем настойчивую подсказку
await ShowTip(weaponTip, ct);
// Ждем пока игрок не поменяет оружие
while (!isWeaponLearned && !ct.IsCancellationRequested)
{
await UniTask.Yield(ct);
}
await HideTip(ct);
isWeaponSwitchRequested = false;
}
await UniTask.Delay(100, cancellationToken: ct);
}
}
#endregion
#region 4. Обучение запросу боеприпасов
private void StartAmmoLearning()
{
ammoCts?.Cancel();
ammoCts?.Dispose();
ammoCts = new CancellationTokenSource();
isAmmoLearningActive = true;
AmmoLearnLoop(ammoCts.Token).Forget();
}
public void OnAmmoEmpty()
{
if (isAmmoLearned) return;
if(!AirDropManager.CanRequestAmmo) return;
ammoEmptyTime = Time.time;
}
public void OnAmmoRequest()
{
if (isAmmoLearned) return;
// Игрок запросил боеприпасы - обучение завершено
isAmmoLearningActive = false;
isAmmoLearned = true;
ammoCts?.Cancel();
StopCurrentView();
Debug.Log("[Learn] Обучение запросу боеприпасов завершено!");
}
private async UniTask AmmoLearnLoop(CancellationToken ct)
{
while (isAmmoLearningActive && !ct.IsCancellationRequested)
{
// Проверяем: патроны кончились и прошло 3 секунды
if (ammoEmptyTime > 0 && Time.time - ammoEmptyTime >= ammoDelay)
{
// Показываем подсказку
await ShowTip(ammoTip, ct);
// Ждем пока игрок не запросит патроны
while (!isAmmoLearned && !ct.IsCancellationRequested)
{
await UniTask.Yield(ct);
}
await HideTip(ct);
ammoEmptyTime = 0;
}
await UniTask.Delay(100, cancellationToken: ct);
}
}
#endregion
#region Вспомогательные методы
private async UniTask ShowTip(Tip tip, CancellationToken ct)
{
// Отменяем текущую подсказку
await StopCurrentView();
viewCts?.Cancel();
viewCts?.Dispose();
viewCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
view.Configure(tip);
await view.Show(viewCts.Token);
}
private async UniTask HideTip(CancellationToken ct)
{
if (viewCts != null && !viewCts.IsCancellationRequested)
{
await view.Hide(viewCts.Token);
}
}
private async UniTask StopCurrentView()
{
if (viewCts != null)
{
viewCts.Cancel();
await view.Hide(viewCts.Token);
viewCts?.Dispose();
viewCts = null;
}
}
#endregion
#region Cleanup
private void OnDestroy()
{
UnsubscribeFromEvents();
shootCts?.Cancel();
diveCts?.Cancel();
weaponCts?.Cancel();
ammoCts?.Cancel();
viewCts?.Cancel();
shootCts?.Dispose();
diveCts?.Dispose();
weaponCts?.Dispose();
ammoCts?.Dispose();
viewCts?.Dispose();
}
#endregion
#region Тестовые методы (для отладки)
[ContextMenu("Reset All Learning")]
public void ResetAllLearning()
{
isShootLearned = false;
isDiveLearned = false;
isWeaponLearned = false;
isAmmoLearned = false;
isDiveLearningActive = false;
isWeaponLearningActive = true;
isAmmoLearningActive = true;
isWeaponSwitchRequested = false;
lastShootTime = Time.time;
weaponEmptyTime = 0;
ammoEmptyTime = 0;
StopCurrentView();
Init();
Debug.Log("[Learn] Все обучения сброшены!");
}
#endregion
}
[Serializable]
public struct Tip
{
[TextArea(2, 4)] public string text;
public InSignal signal;
public AudioClip voiceClip; // Для ситуативной озвучки
}
}