diff --git a/Assets/Images/UI/UI_new/wheel.png b/Assets/Images/UI/UI_new/wheel.png deleted file mode 100644 index 23d35c1a..00000000 Binary files a/Assets/Images/UI/UI_new/wheel.png and /dev/null differ diff --git a/Assets/Images/UI/UI_new/wheel.png.meta b/Assets/Images/UI/UI_new/wheel.png.meta deleted file mode 100644 index 354aa95d..00000000 --- a/Assets/Images/UI/UI_new/wheel.png.meta +++ /dev/null @@ -1,127 +0,0 @@ -fileFormatVersion: 2 -guid: dabfc47e871e05d49b87ab1d70550b0f -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 12 - mipmaps: - mipMapMode: 0 - enableMipMap: 1 - sRGBTexture: 1 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - flipGreenChannel: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - ignoreMipmapLimit: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 0 - wrapV: 0 - wrapW: 0 - nPOTScale: 1 - lightmap: 0 - compressionQuality: 50 - spriteMode: 0 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 1 - alphaIsTransparency: 0 - spriteTessellationDetail: -1 - textureType: 0 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - swizzle: 50462976 - cookieLightType: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Standalone - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Server - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - ignorePlatformSupport: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - nameFileIdTable: {} - mipmapLimitGroupName: - pSDRemoveMatte: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scenes/test.unity b/Assets/Scenes/test.unity index d4f45fcd..d51caf02 100644 --- a/Assets/Scenes/test.unity +++ b/Assets/Scenes/test.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19e3961bc71bd3c6aca088f961e68d81b287abd429e2c03e8a651c37b33e2587 +oid sha256:d97e451eb948620e428b23f0bbecd1228467d09b0b3e52f17175894d43022218 size 5126616 diff --git a/Assets/Scripts/UI/Coin.cs b/Assets/Scripts/UI/Coin.cs deleted file mode 100644 index 6fa0ea55..00000000 --- a/Assets/Scripts/UI/Coin.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Collections; -using UnityEngine; -using TMPro; - -public class Coin : MonoBehaviour -{ - public CoinFlipUI flipUI; - public TextMeshProUGUI coinText; - - [Header("Flying Coins")] - public CoinFlipUI smallCoinPrefab; // префаб монетки (CoinFlipUI) - public int spawnCount = 10; // сколько спавнить - public float spawnRadius = 30f; // разброс старта - public float fallDistance = 100f; // расстояние вниз - public float duration = 0.8f; // длительность анимации - - - public void FreezeCoin() - { - if (flipUI != null) - flipUI.FreezeCoin(); - } - - public void UnfreezeCoin() - { - if (flipUI != null) - flipUI.UnfreezeCoin(); - } - - // Обновляем текст - public void ChangeCoinText(string text) - { - if (coinText != null) - coinText.text = text; - } - - // -------------------- Анимация пополнения -------------------- - public void PlayAddCoinsAnimation() - { - if (smallCoinPrefab == null || flipUI == null) return; - - RectTransform parentRt = flipUI.GetComponent(); - - for (int i = 0; i < spawnCount; i++) - { - CoinFlipUI coin = Instantiate(smallCoinPrefab, parentRt.parent); // спавним в том же контейнере, что и flipUI - RectTransform rt = coin.GetComponent(); - - // стартовая позиция = позиция большой монеты - rt.position = parentRt.position; - - // уменьшаем монетку в 3 раза - rt.localScale = smallCoinPrefab.transform.localScale * 0.33f; - - // добавляем случайное смещение вокруг большой монеты - Vector2 randomOffset = Random.insideUnitCircle * spawnRadius; - rt.anchoredPosition += randomOffset; - - // запускаем анимацию - StartCoroutine(FlyAndShrinkRoutine(rt)); - } - } - - private IEnumerator FlyAndShrinkRoutine(RectTransform rt) - { - Vector2 startPos = rt.anchoredPosition; - Vector2 endPos = startPos - new Vector2(0, fallDistance); - Vector3 startScale = rt.localScale; - Vector3 endScale = Vector3.zero; - - float elapsed = 0f; - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = elapsed / duration; - - rt.anchoredPosition = Vector2.Lerp(startPos, endPos, t); - rt.localScale = Vector3.Lerp(startScale, endScale, t); - - // делаем прозрачность - var img = rt.GetComponent(); - if (img != null) - { - var c = img.color; - c.a = Mathf.Lerp(1f, 0f, t); - img.color = c; - } - - yield return null; - } - - Destroy(rt.gameObject); - } -} diff --git a/Assets/Scripts/UI/Coin.cs.meta b/Assets/Scripts/UI/Coin.cs.meta deleted file mode 100644 index 17874c26..00000000 --- a/Assets/Scripts/UI/Coin.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 615a287435e2fd8488651baff8ccad22 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/UI/CoinFlipUI.cs b/Assets/Scripts/UI/CoinFlipUI.cs deleted file mode 100644 index 17e8b2cd..00000000 --- a/Assets/Scripts/UI/CoinFlipUI.cs +++ /dev/null @@ -1,69 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -public class CoinFlipUI : MonoBehaviour -{ - [Header("UI")] - public Image coinImage; // сам UI-объект с картинкой - public Sprite headsSprite; // орёл - public Sprite tailsSprite; // решка - - [Header("Spin Settings")] - public float spinSpeed = 360f; // градусов в секунду - public bool autoSpin = true; // крутится постоянно - - private bool showingHeads = true; - private Color originalColor; - private bool frozen = false; // флаг «заморозки» - - private void Awake() - { - if (coinImage != null) - originalColor = coinImage.color; // сохраняем исходный цвет - } - - /// Делаем монету серой и останавливаем вращение - public void FreezeCoin() - { - if (coinImage != null) - { - coinImage.color = Color.grey; - coinImage.sprite = headsSprite; // всегда показываем первый спрайт - - // сбрасываем вращение, чтобы монета смотрела "вперёд" - coinImage.rectTransform.localEulerAngles = Vector3.zero; - } - frozen = true; - } - - /// Возвращаем монету в нормальное состояние и включаем вращение - public void UnfreezeCoin() - { - if (coinImage != null) - coinImage.color = originalColor; - - frozen = false; - } - - void Update() - { - if (frozen || !autoSpin) return; - - // крутим монетку - coinImage.rectTransform.Rotate(Vector3.up * spinSpeed * Time.deltaTime); - - float yAngle = coinImage.rectTransform.localEulerAngles.y; - - // меняем спрайт на оборотной стороне - if (yAngle > 90f && yAngle < 270f && showingHeads) - { - coinImage.sprite = tailsSprite; - showingHeads = false; - } - else if ((yAngle <= 90f || yAngle >= 270f) && !showingHeads) - { - coinImage.sprite = headsSprite; - showingHeads = true; - } - } -} diff --git a/Assets/Scripts/UI/CoinFlipUI.cs.meta b/Assets/Scripts/UI/CoinFlipUI.cs.meta deleted file mode 100644 index 19e6f9fb..00000000 --- a/Assets/Scripts/UI/CoinFlipUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2937e80c8d6174347ae0018b6d0b0c3b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/UI/CoinUI.cs b/Assets/Scripts/UI/CoinUI.cs deleted file mode 100644 index e1313cce..00000000 --- a/Assets/Scripts/UI/CoinUI.cs +++ /dev/null @@ -1,73 +0,0 @@ -using UnityEngine; - -/// -/// Отвечает только за отображение и обновление UI монет -/// -public class CoinUI : MonoBehaviour -{ - [SerializeField] private int playerId; - [SerializeField] private Coin coin; - - private int lastCoins; - private bool eventsSubscribed; - - private void OnEnable() - { - ComWrapper.OnInitialize += Subscribe; - - if (!eventsSubscribed && (ComWrapper.onCoinAdded != null || ComWrapper.onCoinRemoved != null)) - Subscribe(); - } - - private void OnDisable() - { - if (eventsSubscribed) - { - if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.RemoveListener(HandleCoinsChanged); - if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.RemoveListener(HandleCoinsChanged); - eventsSubscribed = false; - } - - ComWrapper.OnInitialize -= Subscribe; - } - - private void Subscribe() - { - if (eventsSubscribed) return; - - if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.AddListener(HandleCoinsChanged); - if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.AddListener(HandleCoinsChanged); - - lastCoins = ComWrapper.GetPlayerCredits(playerId); - eventsSubscribed = true; - } - - private void Start() - { - int current = ComWrapper.GetPlayerCredits(playerId); - lastCoins = current; - - if (coin != null) - { - coin.ChangeCoinText($"{current}"); - if (current == 0) coin.FreezeCoin(); - else coin.UnfreezeCoin(); - } - } - - private void HandleCoinsChanged(int playerID, int newAmount) - { - if (coin == null || playerID != playerId) return; - - coin.ChangeCoinText($"{newAmount}"); - - if (newAmount == 0) coin.FreezeCoin(); - else coin.UnfreezeCoin(); - - if (newAmount > lastCoins) - coin.PlayAddCoinsAnimation(); - - lastCoins = newAmount; - } -} - diff --git a/Assets/Scripts/UI/CoinUI.cs.meta b/Assets/Scripts/UI/CoinUI.cs.meta deleted file mode 100644 index 3cd6a3dc..00000000 --- a/Assets/Scripts/UI/CoinUI.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 0b362969ae104ca7bda6aadc36f41dbd -timeCreated: 1770037304 \ No newline at end of file diff --git a/Assets/Scripts/UI/EnemyCardController.cs b/Assets/Scripts/UI/EnemyCardController.cs deleted file mode 100644 index ee77bd65..00000000 --- a/Assets/Scripts/UI/EnemyCardController.cs +++ /dev/null @@ -1,297 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.UI; -using TMPro; - -public class EnemyCardController : MonoBehaviour -{ - [Header("UI Elements")] - public Image avatarImage; - public Animator avatarAnimator; - public TMP_Text nicknameText; - public TMP_Text dialogueText; - public RectTransform dialoguePanel; - - [Header("Settings")] - public float typingSpeed = 0.05f; - public float panelExpandWidth = 300f; - public float panelExpandSpeed = 5f; - - [Header("Animation names")] - public string idleAnimationName = "Idle_bot_1"; - public string transitionIn = "forvard_bot"; - public string transitionOut = "back_bot"; - - private Coroutine panelAnimCoroutine; - private Coroutine textTypingCoroutine; - private Vector2 dialogueOriginalPos; - private WaitForSeconds cachedTypingWait; - private System.Collections.Generic.HashSet availableStates = new System.Collections.Generic.HashSet(); - - private void Awake() - { - if (dialoguePanel != null) - dialogueOriginalPos = dialoguePanel.anchoredPosition; - - cachedTypingWait = new WaitForSeconds(typingSpeed); - ValidateReferences(); - } - - private void OnValidate() - { - ValidateReferences(); - } - - private void ValidateReferences() - { - if (avatarImage == null) Debug.LogWarning($"[{name}] avatarImage не назначен!", this); - if (nicknameText == null) Debug.LogWarning($"[{name}] nicknameText не назначен!", this); - if (dialogueText == null) Debug.LogWarning($"[{name}] dialogueText не назначен!", this); - if (dialoguePanel == null) Debug.LogWarning($"[{name}] dialoguePanel не назначен!", this); - } - - private void OnDisable() - { - StopAllAnimations(); - ResetUIState(); - } - - public void SetEnemy(EnemyData enemy) - { - if (enemy == null) - { - Debug.LogWarning($"[{name}] SetEnemy: enemy is null!"); - return; - } - - if (nicknameText != null) - nicknameText.text = enemy.nickname; - - SetupAnimatorOrSprite(enemy); - ResetUIState(); - - // Устанавливаем порядок панели (только когда объект активен) - if (dialoguePanel != null && gameObject.activeInHierarchy) - dialoguePanel.SetSiblingIndex(0); - } - - private void SetupAnimatorOrSprite(EnemyData enemy) - { - availableStates.Clear(); - - if (enemy.animatorOverride != null && avatarAnimator != null) - { - avatarAnimator.runtimeAnimatorController = enemy.animatorOverride; - avatarAnimator.enabled = true; - - // Кэшируем доступные клипы один раз - var overrides = new System.Collections.Generic.List>(); - enemy.animatorOverride.GetOverrides(overrides); - - foreach (var pair in overrides) - { - if (pair.Value != null) // Есть переопределённый клип - availableStates.Add(pair.Key.name); - } - - SetEmotion(idleAnimationName); - } - else - { - if (avatarAnimator != null) - avatarAnimator.enabled = false; - - if (enemy.characterSprite != null && avatarImage != null) - avatarImage.sprite = enemy.characterSprite; - } - } - - private void ResetUIState() - { - if (dialogueText != null) - dialogueText.text = ""; - - if (dialoguePanel != null) - { - dialoguePanel.anchoredPosition = dialogueOriginalPos; - } - } - - private void StopAllAnimations() - { - if (panelAnimCoroutine != null) - { - StopCoroutine(panelAnimCoroutine); - panelAnimCoroutine = null; - } - - if (textTypingCoroutine != null) - { - StopCoroutine(textTypingCoroutine); - textTypingCoroutine = null; - } - } - - - /// - /// Открывает карточку с анимацией, вызывает callback когда открылась - /// - public void ShowCard(string emotionState, System.Action onShown = null) - { - if (panelAnimCoroutine != null) - StopCoroutine(panelAnimCoroutine); - - panelAnimCoroutine = StartCoroutine(ShowCardCoroutine(emotionState, onShown)); - } - - /// - /// Закрывает карточку с анимацией, вызывает callback когда закрылась - /// - public void HideCard(System.Action onHidden = null) - { - if (panelAnimCoroutine != null) - StopCoroutine(panelAnimCoroutine); - - panelAnimCoroutine = StartCoroutine(HideCardCoroutine(onHidden)); - } - - /// - /// Печатает текст посимвольно, вызывает callback по окончании - /// - public void TypeText(string text, System.Action onComplete = null, bool skipSizePreparation = false) - { - if (textTypingCoroutine != null) - StopCoroutine(textTypingCoroutine); - - textTypingCoroutine = StartCoroutine(TypeTextCoroutine(text, onComplete, skipSizePreparation)); - } - - /// - /// Мгновенно устанавливает текст без анимации печати - /// - public void SetTextImmediate(string text) - { - if (textTypingCoroutine != null) - { - StopCoroutine(textTypingCoroutine); - textTypingCoroutine = null; - } - - if (dialogueText != null) - dialogueText.text = text; - } - - /// - /// Подготавливает размер текста заранее (чтобы избежать "блика" при открытии панели) - /// - public IEnumerator PrepareTextSizeAsync(string text) - { - yield return PrepareTextSize(text); - } - - private IEnumerator ShowCardCoroutine(string emotionState, System.Action onShown) - { - if (dialoguePanel == null) yield break; - - SetEmotion(transitionIn); - - Vector2 hiddenPos = dialogueOriginalPos; - Vector2 shownPos = hiddenPos + new Vector2(panelExpandWidth, 0); - - float t = 0f; - while (t < 1f) - { - t += Time.deltaTime * panelExpandSpeed; - dialoguePanel.anchoredPosition = Vector2.Lerp(hiddenPos, shownPos, t); - yield return null; - } - - dialoguePanel.anchoredPosition = shownPos; - SetEmotion(emotionState); - - onShown?.Invoke(); - } - - private IEnumerator HideCardCoroutine(System.Action onHidden) - { - if (dialoguePanel == null) yield break; - - SetEmotion(transitionOut); - - if (dialogueText != null) - dialogueText.text = ""; - - Vector2 hiddenPos = dialogueOriginalPos; - Vector2 shownPos = hiddenPos + new Vector2(panelExpandWidth, 0); - - float t = 0f; - while (t < 1f) - { - t += Time.deltaTime * panelExpandSpeed; - dialoguePanel.anchoredPosition = Vector2.Lerp(shownPos, hiddenPos, t); - yield return null; - } - - dialoguePanel.anchoredPosition = hiddenPos; - SetEmotion(idleAnimationName); - - onHidden?.Invoke(); - } - - private IEnumerator TypeTextCoroutine(string text, System.Action onComplete, bool skipSizePreparation) - { - if (dialogueText == null) yield break; - - // Подготовка размера шрифта (пропускаем если уже сделано) - if (!skipSizePreparation) - { - yield return PrepareTextSize(text); - } - - // Печать по символам через maxVisibleCharacters (оптимизация) - dialogueText.text = text; - dialogueText.ForceMeshUpdate(); - - int totalChars = dialogueText.textInfo.characterCount; - dialogueText.maxVisibleCharacters = 0; - - for (int i = 0; i <= totalChars; i++) - { - dialogueText.maxVisibleCharacters = i; - yield return cachedTypingWait; - } - - onComplete?.Invoke(); - } - - private IEnumerator PrepareTextSize(string text) - { - if (dialogueText == null) yield break; - - dialogueText.enableWordWrapping = true; - dialogueText.overflowMode = TextOverflowModes.Overflow; - dialogueText.text = text; - dialogueText.enableAutoSizing = true; - dialogueText.fontSizeMin = 5; - - yield return null; // Ждём кадр для расчёта - - float finalFontSize = dialogueText.fontSize; - dialogueText.enableAutoSizing = false; - dialogueText.fontSize = finalFontSize; - dialogueText.text = ""; - } - - - public void SetEmotion(string stateName) - { - if (avatarAnimator == null || !avatarAnimator.enabled || string.IsNullOrEmpty(stateName)) - return; - - if (availableStates.Contains(stateName)) - avatarAnimator.Play(stateName); - } -} - - - diff --git a/Assets/Scripts/UI/EnemyCardController.cs.meta b/Assets/Scripts/UI/EnemyCardController.cs.meta deleted file mode 100644 index 2ee0a79d..00000000 --- a/Assets/Scripts/UI/EnemyCardController.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 19ed6f95566642b4d996b18b1d8d8aa9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/UI/EnemyData.cs b/Assets/Scripts/UI/EnemyData.cs deleted file mode 100644 index 948d5a77..00000000 --- a/Assets/Scripts/UI/EnemyData.cs +++ /dev/null @@ -1,112 +0,0 @@ -using UnityEngine; -using System; - -[CreateAssetMenu(fileName = "NewEnemyData", menuName = "Enemies/EnemyData")] -public class EnemyData : ScriptableObject -{ - public string nickname; - - - - - [Header("Анимации персонажа")] - public AnimatorOverrideController animatorOverride; // для покадровой анимации - public Sprite characterSprite; // текущий/нейтральный спрайт (fallback) - - [Header("Озвучка победы игрока")] - public AudioClip[] playerVictory; - [Header("Озвучка победы бота")] - public AudioClip[] playerDefeat; - - [Header("Фразы для диалога")] - - public DialogueLine[] dialogues; - - [Header("Событийные диалоги")] - public DialogueLine[] onPlayerHit; // Игрок попал - public DialogueLine[] onPlayerMiss; // Игрок промазал - public DialogueLine[] onBotHit; // Бот попал - public DialogueLine[] onBotMiss; // Бот промазал - public DialogueLine[] onPlayerShipDestroyed; // Корабль игрока уничтожен - public DialogueLine[] onBotShipDestroyed; // Корабль бота уничтожен - - // Вспомогательный метод для получения случайной фразы из категории - public DialogueLine? GetRandomDialogue(DialogueEventType eventType) - { - DialogueLine[] sourceArray = null; - switch (eventType) - { - case DialogueEventType.PlayerHit: sourceArray = onPlayerHit; break; - case DialogueEventType.PlayerMiss: sourceArray = onPlayerMiss; break; - case DialogueEventType.BotHit: sourceArray = onBotHit; break; - case DialogueEventType.BotMiss: sourceArray = onBotMiss; break; - case DialogueEventType.PlayerShipDestroyed: sourceArray = onPlayerShipDestroyed; break; - case DialogueEventType.BotShipDestroyed: sourceArray = onBotShipDestroyed; break; - } - - if (sourceArray != null && sourceArray.Length > 0) - { - return sourceArray[UnityEngine.Random.Range(0, sourceArray.Length)]; - } - return null; - } - -} - -public enum DialogueEventType -{ - PlayerHit, - PlayerMiss, - BotHit, - BotMiss, - PlayerShipDestroyed, - BotShipDestroyed -} - -// Enum для имён состояний аниматора -public enum AnimationName -{ - None, - TalkBot1, - TalkBot2, - TalkBot3, - TalkBot4, - AngerBot1, - AngerBot2, - LaughterBot1, - BackBot, - ForwardBot -} - -[System.Serializable] -public struct DialogueLine -{ - [TextArea(2, 5)] - public string text; - - // Теперь используем enum вместо строки — в инспекторе будет выпадающий список - public AnimationName animationName; // например AnimationName.TalkBot1 - - public AudioClip voiceClip; -} - -// Вспомогательный класс, который даёт строковое имя состояния аниматора для каждого enum-члена -public static class AnimationNameExtensions -{ - public static string ToStateName(this AnimationName name) - { - switch (name) - { - case AnimationName.TalkBot1: return "talk_bot_1"; - case AnimationName.TalkBot2: return "talk_bot_2"; - case AnimationName.TalkBot3: return "talk_bot_3"; - case AnimationName.TalkBot4: return "talk_bot_4"; - case AnimationName.AngerBot1: return "anger_bot_1"; - case AnimationName.AngerBot2: return "anger_bot_2"; - case AnimationName.LaughterBot1: return "laughter_bot_1"; - case AnimationName.BackBot: return "back_bot"; - case AnimationName.ForwardBot: return "forward_bot"; - default: return string.Empty; - } - } -} diff --git a/Assets/Scripts/UI/EnemyData.cs.meta b/Assets/Scripts/UI/EnemyData.cs.meta deleted file mode 100644 index 009f3480..00000000 --- a/Assets/Scripts/UI/EnemyData.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: aad3df3b54a3fd64d8288b172c35ad49 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/UI/EnemyManager.cs b/Assets/Scripts/UI/EnemyManager.cs deleted file mode 100644 index d3d80df8..00000000 --- a/Assets/Scripts/UI/EnemyManager.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -public class EnemyManager : MonoBehaviour -{ - [Header("Список врагов")] - public EnemyData[] enemies; - - [Header("UI контроллер карточки")] - public EnemyCardController cardController; - - [Header("Settings")] - public float dialogueHoldTime = 1f; - - private int currentIndex = 0; - private int playerId = 1; - private Coroutine currentDialogueCoroutine; - private bool isDialoguePlaying = false; - - // Отслеживание использованных реплик для каждого типа события - private Dictionary> usedDialogueIndices = new Dictionary>(); - - private void Start() - { - var player = gameObject.GetComponentInParent(); - if (player != null) - playerId = player.PlayerId; - else - Debug.LogWarning($"[{name}] Player не найден в родителях!"); - } - - public void SetEnemy(int index) - { - if (enemies == null || index < 0 || index >= enemies.Length) - { - Debug.LogWarning($"[{name}] SetEnemy: invalid index {index}"); - return; - } - - if (!cardController.gameObject.activeSelf) - cardController.gameObject.SetActive(true); - - currentIndex = index; - cardController.SetEnemy(enemies[index]); - isDialoguePlaying = false; - - // Сбрасываем историю использованных реплик при смене врага - usedDialogueIndices.Clear(); - } - - public void SetEnemyPlayer() - { - // Профили игроков - это последние 2 элемента в массиве enemies - // playerId = 1 -> enemies[Length-2], playerId = 2 -> enemies[Length-1] - if (enemies == null || enemies.Length < 2) - { - Debug.LogWarning($"[{name}] SetEnemyPlayer: недостаточно врагов в массиве для профилей игроков!"); - cardController.gameObject.SetActive(false); - return; - } - - int playerProfileIndex = enemies.Length - (3 - playerId); // playerId 1 -> -2, playerId 2 -> -1 - - if (playerProfileIndex < 0 || playerProfileIndex >= enemies.Length) - { - Debug.LogWarning($"[{name}] SetEnemyPlayer: некорректный индекс профиля игрока {playerProfileIndex}"); - cardController.gameObject.SetActive(false); - return; - } - - currentIndex = playerProfileIndex; - - // Скрываем карточку (игрок не показывает UI) - cardController.gameObject.SetActive(false); - - isDialoguePlaying = false; - - // Сбрасываем историю использованных реплик - usedDialogueIndices.Clear(); - } - - /// - /// Показывает диалог с карточкой: открывает ? говорит ? закрывает - /// Если карточка неактивна - просто играет звук - /// - public void ShowDialogueWithCard(DialogueLine line) - { - // Если карточка неактивна - просто играем звук - if (cardController == null || !cardController.gameObject.activeInHierarchy) - { - PlayVoiceOnly(line.voiceClip); - return; - } - - - if (currentDialogueCoroutine != null) - StopCoroutine(currentDialogueCoroutine); - - currentDialogueCoroutine = StartCoroutine(DialogueWithCardSequence(line)); - } - - /// - /// Проигрывает только звук без карточки - /// - public void PlayVoiceOnly(AudioClip clip) - { - if (clip != null && SoundManager.Instance != null) - { - SoundManager.Instance.PlayVoice(playerId, clip); - } - } - - /// - /// Показывает текст на уже открытой карточке (без анимации открытия/закрытия) - /// - public void ShowTextOnly(string text, string emotion = null) - { - if (!string.IsNullOrEmpty(emotion)) - cardController.SetEmotion(emotion); - - cardController.TypeText(text); - } - - private IEnumerator DialogueWithCardSequence(DialogueLine line) - { - isDialoguePlaying = true; - - // 1. Подготавливаем размер текста ДО открытия панели (чтобы не было "блика") - yield return cardController.PrepareTextSizeAsync(line.text); - - // 2. Открываем карточку с эмоцией перехода - bool cardOpened = false; - cardController.ShowCard(line.animationName.ToStateName(), () => cardOpened = true); - - // Ждём, пока карточка откроется - yield return new WaitUntil(() => cardOpened); - - // 3. Играем звук - if (line.voiceClip != null && SoundManager.Instance != null) - { - SoundManager.Instance.PlayVoice(playerId, line.voiceClip); - } - - // 4. Печатаем текст (размер уже подготовлен) - bool textComplete = false; - cardController.TypeText(line.text, () => textComplete = true, skipSizePreparation: true); - - // Ждём, пока текст напечатается - yield return new WaitUntil(() => textComplete); - - // 4. Держим текст немного - yield return new WaitForSeconds(dialogueHoldTime); - - // 5. Закрываем карточку - bool cardClosed = false; - cardController.HideCard(() => cardClosed = true); - - yield return new WaitUntil(() => cardClosed); - - isDialoguePlaying = false; - } - - /// - /// Вызывает диалог реакции на событие (с карточкой) - /// Выбирает реплику которая ещё не использовалась, пока все не кончатся - /// - public void TriggerEventReaction(DialogueEventType eventType) - { - if (enemies == null || currentIndex < 0 || currentIndex >= enemies.Length) - return; - - if(isDialoguePlaying) return; - - var enemy = enemies[currentIndex]; - DialogueLine? line = GetNonRepeatingDialogue(enemy, eventType); - - if (line.HasValue) - { - ShowDialogueWithCard(line.Value); - } - } - - /// - /// Получает случайную реплику которая ещё не использовалась для данного типа события - /// - private DialogueLine? GetNonRepeatingDialogue(EnemyData enemy, DialogueEventType eventType) - { - if (enemy == null) - return null; - - // Получаем нужный массив реплик по типу события - DialogueLine[] sourceArray = null; - switch (eventType) - { - case DialogueEventType.PlayerHit: sourceArray = enemy.onPlayerHit; break; - case DialogueEventType.PlayerMiss: sourceArray = enemy.onPlayerMiss; break; - case DialogueEventType.BotHit: sourceArray = enemy.onBotHit; break; - case DialogueEventType.BotMiss: sourceArray = enemy.onBotMiss; break; - case DialogueEventType.PlayerShipDestroyed: sourceArray = enemy.onPlayerShipDestroyed; break; - case DialogueEventType.BotShipDestroyed: sourceArray = enemy.onBotShipDestroyed; break; - } - - if (sourceArray == null || sourceArray.Length == 0) - return null; - - // Инициализируем список использованных реплик для этого типа события если нужно - if (!usedDialogueIndices.ContainsKey(eventType)) - { - usedDialogueIndices[eventType] = new List(); - } - - var usedList = usedDialogueIndices[eventType]; - - // Создаём список неиспользованных индексов - var unusedIndices = new List(); - for (int i = 0; i < sourceArray.Length; i++) - { - if (!usedList.Contains(i)) - unusedIndices.Add(i); - } - - // Если все реплики использованы - сбрасываем и начинаем заново - if (unusedIndices.Count == 0) - { - usedList.Clear(); - for (int i = 0; i < sourceArray.Length; i++) - { - unusedIndices.Add(i); - } - } - - // Выбираем случайный неиспользованный индекс - int randomIndex = unusedIndices[Random.Range(0, unusedIndices.Count)]; - usedList.Add(randomIndex); - - return sourceArray[randomIndex]; - } - - /// - /// Реакция на конец игры (только звук, без карточки) - /// - public void TriggerEndGameReaction(bool playerWon) - { - if (enemies == null || enemies.Length == 0) return; - if (currentIndex < 0 || currentIndex >= enemies.Length) return; - isDialoguePlaying = true; - - var enemy = enemies[currentIndex]; - var clips = playerWon ? enemy.playerVictory : enemy.playerDefeat; - - if (clips == null || clips.Length == 0) return; - - // Останавливаем голосовой канал текущего игрока, затем проигрываем финальную фразу - if (SoundManager.Instance != null) - { - SoundManager.Instance.StopVoice(playerId); - } - Debug.Log(clips[0].name); - - PlayVoiceOnly(clips[Random.Range(0, clips.Length)]); - } - - public EnemyData GetCurrentEnemy() - { - if (enemies == null || currentIndex < 0 || currentIndex >= enemies.Length) - return null; - - return enemies[currentIndex]; - } -} diff --git a/Assets/Scripts/UI/EnemyManager.cs.meta b/Assets/Scripts/UI/EnemyManager.cs.meta deleted file mode 100644 index 67b0f4be..00000000 --- a/Assets/Scripts/UI/EnemyManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ab306eb3313a66f4291adfc45803dcb7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/UI/GameOver.cs b/Assets/Scripts/UI/GameOver.cs deleted file mode 100644 index 934ab6dd..00000000 --- a/Assets/Scripts/UI/GameOver.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -public class GameOver : MonoBehaviour -{ - [SerializeField] private Sprite[] sprites; - public Image image; // Это Image на сцене, куда вставится спрайт - - public void GameOverSwitch(bool victory) - { - image.sprite = victory ? sprites[0] : sprites[1]; - } -} diff --git a/Assets/Scripts/UI/GameOver.cs.meta b/Assets/Scripts/UI/GameOver.cs.meta deleted file mode 100644 index af944671..00000000 --- a/Assets/Scripts/UI/GameOver.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f543ea72d7834b645bd21c1abd0852d5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/UI/GameOverUI.cs b/Assets/Scripts/UI/GameOverUI.cs deleted file mode 100644 index 3c5b9b35..00000000 --- a/Assets/Scripts/UI/GameOverUI.cs +++ /dev/null @@ -1,102 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; -using TMPro; - -/// -/// Отвечает за отображение экрана GameOver и обновление кнопок -/// -public class GameOverUI : MonoBehaviour -{ - [SerializeField] private int playerId; - [SerializeField] private GameManager gameManager; - - [Header("UI Elements")] - [SerializeField] private Button replayButton; - [SerializeField] private Button continueButton; - [SerializeField] private TextMeshProUGUI resultText; - [SerializeField] private GameObject uiParticle; - [SerializeField] private GameOver gameOverStatusImage; - - public void Show(bool isWin, bool isBotGame) - { - if (uiParticle != null) - uiParticle.SetActive(isWin); - - if (resultText != null) - { - resultText.text = isWin ? "Хорош!" : "Увы..."; - resultText.color = isWin ? new Color(1f, 0.84f, 0f) : Color.white; - - if (gameOverStatusImage != null) - gameOverStatusImage.GameOverSwitch(isWin); - } - - int credits = ComWrapper.GetPlayerCredits(playerId); - - if (continueButton != null) continueButton.gameObject.SetActive(false); - if (replayButton != null) replayButton.gameObject.SetActive(false); - - if (isBotGame) - { - ShowBotGameButtons(isWin, credits); - } - else - { - ShowPvPGameButtons(credits); - } - } - - private void ShowBotGameButtons(bool isWin, int credits) - { - if (isWin) - { - if (continueButton != null) - { - continueButton.gameObject.SetActive(true); - continueButton.interactable = true; - } - } - else - { - if (replayButton != null) - { - replayButton.gameObject.SetActive(true); - replayButton.interactable = credits >= 1; - var txt = replayButton.GetComponentInChildren(); - if (txt != null) txt.text = "Переиграть"; - } - } - } - - private void ShowPvPGameButtons(int credits) - { - if (replayButton != null) - { - int oponentId = playerId == 1 ? 2 : 1; - int oponentCredits = ComWrapper.GetPlayerCredits(oponentId); - - replayButton.gameObject.SetActive(true); - replayButton.interactable = credits >= 2 || (credits >= 1 && oponentCredits >= 1); - var txt = replayButton.GetComponentInChildren(); - if (txt != null) txt.text = "Ещё раз!"; - } - } - - public void UpdateButtons() - { - int credits = ComWrapper.GetPlayerCredits(playerId); - int oponentId = playerId == 1 ? 2 : 1; - int oponentCredits = ComWrapper.GetPlayerCredits(oponentId); - - if (continueButton != null && continueButton.gameObject.activeSelf) - continueButton.interactable = credits >= 1; - - if (replayButton != null && replayButton.gameObject.activeSelf) - { - if (gameManager != null && gameManager.gameMode == GameMode.PlayerVsBot) - replayButton.interactable = credits >= 1; - else - replayButton.interactable = credits >= 2 || (credits >= 1 && oponentCredits >= 1); - } - } -} diff --git a/Assets/Scripts/UI/GameOverUI.cs.meta b/Assets/Scripts/UI/GameOverUI.cs.meta deleted file mode 100644 index 5abcd18b..00000000 --- a/Assets/Scripts/UI/GameOverUI.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: da85480a628a444182a5c189091dc623 -timeCreated: 1770037344 \ No newline at end of file diff --git a/Assets/Scripts/UI/MainMenuButtonsUpdater.cs b/Assets/Scripts/UI/MainMenuButtonsUpdater.cs deleted file mode 100644 index 25410057..00000000 --- a/Assets/Scripts/UI/MainMenuButtonsUpdater.cs +++ /dev/null @@ -1,74 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -/// -/// Отвечает за обновление interactable состояния кнопок главного меню по балансу -/// -public class MainMenuButtonsUpdater : MonoBehaviour -{ - [SerializeField] private int playerId; - [SerializeField] private Button playVsBotButton; - [SerializeField] private Button playVsPlayerButton; - - private bool eventsSubscribed; - - private void OnEnable() - { - ComWrapper.OnInitialize += Subscribe; - - if (!eventsSubscribed && (ComWrapper.onCoinAdded != null || ComWrapper.onCoinRemoved != null)) - Subscribe(); - } - - private void OnDisable() - { - if (eventsSubscribed) - { - if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.RemoveListener(OnCoinsChanged); - if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.RemoveListener(OnCoinsChanged); - eventsSubscribed = false; - } - - ComWrapper.OnInitialize -= Subscribe; - } - - private void Subscribe() - { - if (eventsSubscribed) return; - - if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.AddListener(OnCoinsChanged); - if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.AddListener(OnCoinsChanged); - - eventsSubscribed = true; - UpdateButtons(); - } - - private void Start() - { - UpdateButtons(); - } - - private void OnCoinsChanged(int playerID, int newAmount) - { - UpdateButtons(); - } - - public void UpdateButtons() - { - int myCoins = ComWrapper.GetPlayerCredits(playerId); - int enemyId = playerId == 1 ? 2 : 1; - int enemyCoins = ComWrapper.GetPlayerCredits(enemyId); - - bool freePlay = SettingsManager.instance != null && - SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл"; - - if (playVsBotButton != null) - playVsBotButton.interactable = myCoins >= 1 || freePlay; - - bool bothHaveEnough = myCoins >= 2 || (myCoins >= 1 && enemyCoins >= 1) || freePlay; - - if (playVsPlayerButton != null) - playVsPlayerButton.interactable = bothHaveEnough; - } -} - diff --git a/Assets/Scripts/UI/MainMenuButtonsUpdater.cs.meta b/Assets/Scripts/UI/MainMenuButtonsUpdater.cs.meta deleted file mode 100644 index bba7a4af..00000000 --- a/Assets/Scripts/UI/MainMenuButtonsUpdater.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 868ec3b4c73d4bddaffbbec6ed83002a -timeCreated: 1770037344 \ No newline at end of file diff --git a/Assets/Scripts/UI/PlacementChoiceOverlay.cs b/Assets/Scripts/UI/PlacementChoiceOverlay.cs deleted file mode 100644 index 20c89281..00000000 --- a/Assets/Scripts/UI/PlacementChoiceOverlay.cs +++ /dev/null @@ -1,51 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -public class PlacementChoiceOverlay : MonoBehaviour -{ - [SerializeField] private GameManager gameManager; - [SerializeField] private UIManager uiManager; - [SerializeField] private Button autoButton; - [SerializeField] private Button manualButton; - - [Header("Optional focus after auto")] - [SerializeField] private Button focusAfterAuto; // кнопка "в бой" на экране расстановки - - private void Awake() - { - if (autoButton != null) autoButton.onClick.AddListener(OnAuto); - if (manualButton != null) manualButton.onClick.AddListener(OnManual); - - } - - private void OnAuto() - { - if (gameManager == null || uiManager == null) return; - - uiManager.HideAllOverlays(); - gameManager.SetState(GameState.ShipPlacement); - gameManager.SetInputMode(GameManager.InputMode.UI); - - // Авто-расстановка - gameManager.ArrangeGame(); - - // Навести курсор на кнопку "в бой" - if (focusAfterAuto != null) - { - var highlight = focusAfterAuto.GetComponent(); - if (highlight != null) - highlight.SetHighlighted(true); - } - } - - private void OnManual() - { - if (gameManager == null || uiManager == null) return; - - uiManager.HideAllOverlays(); - gameManager.SetState(GameState.ShipPlacement); - gameManager.SetInputMode(GameManager.InputMode.ShipPlacement); - gameManager.InitTargetCursor(true); - // Ничего не расставляем — игрок делает сам - } -} \ No newline at end of file diff --git a/Assets/Scripts/UI/SmoothSlider.cs b/Assets/Scripts/UI/SmoothSlider.cs deleted file mode 100644 index c60a404e..00000000 --- a/Assets/Scripts/UI/SmoothSlider.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.UI; - -/// -/// Компонент для плавного изменения значения Slider. -/// Вызывай метод AnimateTo() напрямую для анимации. -/// -[RequireComponent(typeof(Slider))] -public class SmoothSlider : MonoBehaviour -{ - [Header("Настройки плавности")] - [Tooltip("Длительность анимации в секундах")] - [SerializeField] private float duration = 0.3f; - - [Tooltip("Кривая анимации (по умолчанию - плавная)")] - [SerializeField] private AnimationCurve curve = AnimationCurve.EaseInOut(0, 0, 1, 1); - - private Slider slider; - private Coroutine animationCoroutine; - private float targetValue; - private System.Action pendingCallback; - private int animationId; // Уникальный ID для отслеживания отмененных анимаций - - private void Awake() - { - slider = GetComponent(); - targetValue = slider.value; - animationId = 0; - } - - /// - /// Плавно анимирует слайдер к новому значению. - /// В конце анимации вызовет callback onComplete. - /// - public void AnimateTo(float endValue, System.Action onComplete = null) - { - if (animationCoroutine != null) - { - StopCoroutine(animationCoroutine); - - // ВАЖНО: Сохраняем СТАРОЕ целевое значение и callback ПЕРЕД обновлением - float previousTarget = targetValue; - System.Action previousCallback = pendingCallback; - - // Устанавливаем финальное значение предыдущей анимации - slider.value = previousTarget; - - // Вызываем callback с правильным финальным значением - previousCallback?.Invoke(previousTarget); - } - - // Теперь обновляем на новые значения - targetValue = endValue; - pendingCallback = onComplete; - animationId++; // Новый ID для новой анимации - animationCoroutine = StartCoroutine(AnimateToValue(endValue, onComplete, animationId)); - } - - private IEnumerator AnimateToValue(float endValue, System.Action onComplete, int myAnimationId) - { - float startValue = slider.value; - float elapsed = 0f; - - while (elapsed < duration) - { - elapsed += Time.unscaledDeltaTime; - float t = Mathf.Clamp01(elapsed / duration); - - slider.value = Mathf.Lerp(startValue, endValue, curve.Evaluate(t)); - yield return null; - } - - // Финальное значение - только если анимация не была отменена - if (myAnimationId == animationId) - { - slider.value = endValue; - targetValue = endValue; - onComplete?.Invoke(endValue); - - animationCoroutine = null; - } - } -} - diff --git a/Assets/Scripts/UI/SmoothSlider.cs.meta b/Assets/Scripts/UI/SmoothSlider.cs.meta deleted file mode 100644 index 1411a219..00000000 --- a/Assets/Scripts/UI/SmoothSlider.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5e80540a01c44b2e9f69fbf8a703cee6 -timeCreated: 1765969628 \ No newline at end of file diff --git a/Assets/Scripts/UI/UIPanelId.cs b/Assets/Scripts/UI/UIPanelId.cs deleted file mode 100644 index 5441a682..00000000 --- a/Assets/Scripts/UI/UIPanelId.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UnityEngine; - -public enum UIPanelLayer { Screen, Overlay } - -public class UIPanelId : MonoBehaviour -{ - public string id; // например "PlacementHint" - public UIPanelLayer layer; // Screen или Overlay -} \ No newline at end of file