using UnityEngine;

/// <summary>
/// Класс для предмета "Мяч".
/// </summary>
[CreateAssetMenu(fileName = "NewBall", menuName = "Инвентарь/Предметы/Мяч")]
public class Ball : Item
{
    [Header("Параметры мяча")]
    public float throwForce = 500f; // Сила броска

    /// <summary>
    /// Переопределенный метод использования мяча.
    /// </summary>
    /// <param name="player">Игрок, использующий мяч.</param>
    public override void Use(Player player)
    {
        player.ThrowItem(this);
        Debug.Log("Брослен мяч");
    }
}

using UnityEngine;

/// <summary>
/// Класс для предмета "Аптечка".
/// Восстанавливает здоровье.
/// </summary>
[CreateAssetMenu(fileName = "NewFirstAidKit", menuName = "Инвентарь/Предметы/Аптечка")]
public class FirstAidKit : Item
{
    [Header("Параметры аптечки")]
    public int healthRestore = 50; // Восстановление здоровья

    /// <summary>
    /// Переопределенный метод использования аптечки.
    /// </summary>
    /// <param name="player">Игрок, использующий аптечку.</param>
    public override void Use(Player player)
    {
        player.RestoreHealth(healthRestore);
        Debug.Log($"Использована аптечка: восстановлено {healthRestore} HP");
    }
}
using UnityEngine;

/// <summary>
/// Базовый класс для всех предметов инвентаря.
/// </summary>
[CreateAssetMenu(fileName = "NewItem", menuName = "Инвентарь/Предмет")]
public class Item : ScriptableObject
{
    [Header("Основные параметры")]
    public string itemName; // Название предмета
    public Sprite icon; // Иконка предмета
    public ItemType type; // Тип предмета

    /// <summary>
    /// Метод использования предмета. Может быть переопределен в наследниках.
    /// </summary>
    /// <param name="player">Игрок, использующий предмет.</param>
    public virtual void Use(Player player)
    {
        Debug.Log("Использован предмет: " + itemName);
    }
}

/// <summary>
/// Типы предметов, доступные в инвентаре.
/// </summary>
public enum ItemType
{
    Consumable, // Расходуемый
    Throwable,  // Бросаемый
    Equipment,  // Снаряжение
    Misc        // Разное
}

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Класс пула объектов для оптимизации создания и уничтожения объектов.
/// </summary>
public class ObjectPool : MonoBehaviour
{
    // Singleton экземпляр пула объектов
    public static ObjectPool Instance { get; private set; }

    [Header("Настройки пула объектов")]
    [Tooltip("Максимальное количество объектов в пуле для каждого префаба.")]
    public int poolSize = 20;

    // Словарь для хранения пулов по названию префаба
    private Dictionary<string, Queue<GameObject>> poolDictionary;

    private void Awake()
    {
        // Реализация Singleton паттерна
        if (Instance != null && Instance != this)
        {
            Debug.LogWarning("Обнаружено несколько экземпляров ObjectPool! Оставляем первый.");
            Destroy(this);
            return;
        }
        Instance = this;

        poolDictionary = new Dictionary<string, Queue<GameObject>>();

        // Инициализация пулов для BallPrefab и WorldItemPrefab
        InitializePool("BallPrefab");
        InitializePool("WorldItemPrefab");
    }

    /// <summary>
    /// Метод для инициализации пула для конкретного префаба.
    /// </summary>
    /// <param name="prefabName">Название префаба.</param>
    private void InitializePool(string prefabName)
    {
        GameObject prefab = Resources.Load<GameObject>(prefabName);
        if (prefab == null)
        {
            Debug.LogError($"Префаб с именем {prefabName} не найден в папке Resources.");
            return;
        }

        Queue<GameObject> objectPool = new Queue<GameObject>();

        for (int i = 0; i < poolSize; i++)
        {
            GameObject obj = Instantiate(prefab);
            obj.SetActive(false);
            objectPool.Enqueue(obj);
        }

        poolDictionary.Add(prefabName, objectPool);
    }

    /// <summary>
    /// Метод для получения объекта из пула.
    /// </summary>
    /// <param name="prefabName">Название префаба.</param>
    /// <returns>Активный объект из пула или null, если пул пуст.</returns>
    public GameObject GetPooledObject(string prefabName)
    {
        if (!poolDictionary.ContainsKey(prefabName))
        {
            Debug.LogWarning($"Пул для префаба {prefabName} не существует.");
            return null;
        }

        Queue<GameObject> objectPool = poolDictionary[prefabName];
        if (objectPool.Count > 0)
        {
            GameObject obj = objectPool.Dequeue();
            return obj;
        }
        else
        {
            Debug.LogWarning($"Пул для префаба {prefabName} пуст.");
            return null;
        }
    }

    /// <summary>
    /// Метод для возврата объекта в пул.
    /// </summary>
    /// <param name="prefabName">Название префаба.</param>
    /// <param name="obj">Объект для возврата.</param>
    public void ReturnToPool(string prefabName, GameObject obj)
    {
        if (!poolDictionary.ContainsKey(prefabName))
        {
            Debug.LogWarning($"Пул для префаба {prefabName} не существует.");
            return;
        }

        obj.SetActive(false);
        poolDictionary[prefabName].Enqueue(obj);
    }
}
using UnityEngine;

/// <summary>
/// Класс для предмета "Газировка".
/// </summary>
[CreateAssetMenu(fileName = "NewSoda", menuName = "Инвентарь/Предметы/Газировка")]
public class Soda : Item
{
    [Header("Параметры газировки")]
    public int energyRestore = 20; // Восстановление энергии
    public int healthPenalty = 5;  // Потеря здоровья

    /// <summary>
    /// Переопределенный метод использования газировки.
    /// </summary>
    /// <param name="player">Игрок, использующий газировку.</param>
    public override void Use(Player player)
    {
        player.RestoreEnergy(energyRestore);
        player.TakeDamage(healthPenalty);
        Debug.Log($"Использована газировка: +{energyRestore} энергии, -{healthPenalty} HP");
    }
}

using UnityEngine;

/// <summary>
/// Класс для предмета "Скотч".
/// Используется для ремонта сломанных устройств.
/// </summary>
[CreateAssetMenu(fileName = "NewTape", menuName = "Инвентарь/Предметы/Скотч")]
public class Tape : Item
{
    [Header("Параметры скотча")]
    public int repairAmount = 50; // Количество восстановления прочности

    /// <summary>
    /// Переопределенный метод использования скотча.
    /// </summary>
    /// <param name="player">Игрок, использующий скотч.</param>
    public override void Use(Player player)
    {
        player.RepairDevice(repairAmount);
        Debug.Log($"Использован скотч: восстановлено {repairAmount} прочности");
    }
}


using UnityEngine;

/// <summary>
/// Класс для предмета "Записная книжка".
/// Содержит подсказки и изученные формулы.
/// </summary>
[CreateAssetMenu(fileName = "NewNotebook", menuName = "Инвентарь/Предметы/Записная Книжка")]
public class Notebook : Item
{
    [Header("Содержимое записной книжки")]
    [TextArea]
    public string notes; // Текст подсказок и формул

    /// <summary>
    /// Переопределенный метод использования записной книжки.
    /// </summary>
    /// <param name="player">Игрок, использующий записную книжку.</param>
    public override void Use(Player player)
    {
        // Отображение содержимого записной книжки
        UIManager.Instance.ShowNotebook(notes);
        Debug.Log("Использована записная книжка");
    }
}
using UnityEngine;

/// <summary>
/// Класс для предметов, расположенных в игровом мире.
/// </summary>
public class WorldItem : MonoBehaviour
{
    [Header("Предмет")]
    public Item item;

    [Header("Количество")]
    public int amount = 1;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            if (Inventory.Instance.Add(item, amount))
            {
                // Возврат объекта в пул
                ObjectPool.Instance.ReturnToPool("WorldItemPrefab", gameObject);
            }
        }
    }
}

using UnityEngine;

/// <summary>
/// Класс для представления вопроса в системе QA.
/// </summary>
[System.Serializable]
public class Question
{
    public string questionText; // Текст вопроса
    public QuestionType questionType; // Тип вопроса

    // Для вопросов с выбором ответа
    [Header("Вопрос с выбором ответа")]
    public string[] choices;
    public int correctChoiceIndex;

    // Для вопросов с вводом ответа
    [Header("Вопрос с вводом ответа")]
    public string correctAnswer;
}
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Класс для управления системой вопросов и ответов (QA).
/// </summary>
public class QuestionManager : MonoBehaviour
{
    public static QuestionManager Instance;

    [Header("Список вопросов")]
    public List<Question> questions;

    private int currentQuestionIndex = -1;

    private void Awake()
    {
        // Реализация Singleton паттерна
        if (Instance != null && Instance != this)
        {
            Debug.LogWarning("Обнаружено несколько экземпляров QuestionManager! Оставляем первый.");
            Destroy(this);
            return;
        }
        Instance = this;
    }

    /// <summary>
    /// Метод для получения случайного вопроса.
    /// </summary>
    /// <returns>Случайный вопрос.</returns>
    public Question GetRandomQuestion()
    {
        if (questions.Count == 0)
            return null;

        currentQuestionIndex = Random.Range(0, questions.Count);
        return questions[currentQuestionIndex];
    }

    /// <summary>
    /// Метод для проверки ответа на текущий вопрос.
    /// </summary>
    /// <param name="selectedChoiceIndex">Выбранный индекс для вопросов с выбором ответа.</param>
    /// <param name="inputAnswer">Введенный ответ для вопросов с вводом ответа.</param>
    /// <returns>Правильный ли ответ.</returns>
    public bool CheckAnswer(int selectedChoiceIndex, string inputAnswer)
    {
        if (currentQuestionIndex < 0 || currentQuestionIndex >= questions.Count)
            return false;

        Question currentQuestion = questions[currentQuestionIndex];
        if (currentQuestion.questionType == QuestionType.MultipleChoice)
        {
            return selectedChoiceIndex == currentQuestion.correctChoiceIndex;
        }
        else if (currentQuestion.questionType == QuestionType.Input)
        {
            return currentQuestion.correctAnswer.ToLower() == inputAnswer.ToLower();
        }

        return false;
    }
}

/// <summary>
/// Типы вопросов, доступные в системе QA.
/// </summary>
public enum QuestionType
{
    MultipleChoice, // Вопрос с выбором ответа
    Input           // Вопрос с вводом ответа
}
using UnityEngine;

/// <summary>
/// Класс для активации вопросов при взаимодействии с игроком.
/// </summary>
public class QuestionTrigger : MonoBehaviour
{
    [Header("Вопрос, который будет задан")]
    public Question question;

    [Header("Ссылка на UI вопроса")]
    public QuestionUI questionUI;

    private bool isTriggered = false;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (!isTriggered && other.CompareTag("Player"))
        {
            isTriggered = true;
            ShowQuestion();
        }
    }

    private void ShowQuestion()
    {
        if (questionUI != null && question != null)
        {
            questionUI.ShowQuestion(question);
        }
        else
        {
            Debug.LogWarning("Не назначены ссылки на QuestionUI или Question.");
        }
    }
}

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Класс управления UI системы вопросов и ответов (QA).
/// </summary>
public class QuestionUI : MonoBehaviour
{
    [Header("Элементы UI")]
    [Tooltip("Панель вопросов.")]
    public GameObject questionPanel;

    [Tooltip("Текст вопроса.")]
    public Text questionText;

    [Tooltip("Кнопки выбора для вопросов с выбором ответа.")]
    public Button[] choiceButtons;

    [Tooltip("Поле ввода для вопросов с вводом ответа.")]
    public InputField answerInputField;

    [Tooltip("Кнопка подтверждения ответа для вопросов с вводом.")]
    public Button submitAnswerButton;

    private void Start()
    {
        questionPanel.SetActive(false);
    }

    /// <summary>
    /// Метод для отображения вопроса.
    /// </summary>
    /// <param name="question">Вопрос для отображения.</param>
    public void ShowQuestion(Question question)
    {
        questionPanel.SetActive(true);
        questionText.text = question.questionText;

        if (question.questionType == QuestionType.MultipleChoice)
        {
            // Настройка кнопок выбора
            for (int i = 0; i < choiceButtons.Length; i++)
            {
                if (i < question.choices.Length)
                {
                    choiceButtons[i].gameObject.SetActive(true);
                    choiceButtons[i].GetComponentInChildren<Text>().text = question.choices[i];
                    int index = i; // Capture the current value of i
                    choiceButtons[i].onClick.RemoveAllListeners();
                    choiceButtons[i].onClick.AddListener(() => SubmitMultipleChoiceAnswer(index));
                }
                else
                {
                    choiceButtons[i].gameObject.SetActive(false);
                }
            }

            // Отключение поля ввода
            answerInputField.gameObject.SetActive(false);
            submitAnswerButton.gameObject.SetActive(false);
        }
        else if (question.questionType == QuestionType.Input)
        {
            // Отключение кнопок выбора
            foreach (var button in choiceButtons)
            {
                button.gameObject.SetActive(false);
            }

            // Включение поля ввода
            answerInputField.gameObject.SetActive(true);
            submitAnswerButton.gameObject.SetActive(true);
            answerInputField.text = "";
        }
    }

    /// <summary>
    /// Метод для скрытия панели вопросов.
    /// </summary>
    public void HideQuestion()
    {
        questionPanel.SetActive(false);
    }

    /// <summary>
    /// Метод для отправки ответа на вопрос с выбором.
    /// </summary>
    /// <param name="choiceIndex">Индекс выбранного ответа.</param>
    private void SubmitMultipleChoiceAnswer(int choiceIndex)
    {
        bool isCorrect = QuestionManager.Instance.CheckAnswer(choiceIndex, null);
        if (isCorrect)
        {
            Debug.Log("Правильный ответ!");
            // Можно добавить награды или другие действия
        }
        else
        {
            Debug.Log("Неправильный ответ!");
            // Можно добавить штрафы или повтор вопроса
        }
        HideQuestion();
    }

    /// <summary>
    /// Метод для отправки ответа на вопрос с вводом.
    /// </summary>
    public void SubmitInputAnswer()
    {
        string userAnswer = answerInputField.text;
        if (string.IsNullOrEmpty(userAnswer))
        {
            Debug.LogWarning("Ответ не может быть пустым.");
            return;
        }

        bool isCorrect = QuestionManager.Instance.CheckAnswer(-1, userAnswer);
        if (isCorrect)
        {
            Debug.Log("Правильный ответ!");
            // Можно добавить награды или другие действия
        }
        else
        {
            Debug.Log("Неправильный ответ!");
            // Можно добавить штрафы или повтор вопроса
        }
        HideQuestion();
    }
}
using UnityEngine;

/// <summary>
/// Класс игрока с основными параметрами и методами.
/// </summary>
public class Player : MonoBehaviour
{
    [Header("Параметры игрока")]
    public int health = 100;
    public int energy = 100;

    [Header("Префабы")]
    [Tooltip("Префаб мяча для броска.")]
    public GameObject ballPrefab;

    [Tooltip("Префаб предмета для выброса.")]
    public GameObject worldItemPrefab;

    [Header("Настройки броска")]
    public float throwForce = 500f; // Сила броска

    private void Start()
    {
        // Инициализация компонентов при необходимости
    }

    /// <summary>
    /// Восстановление энергии игрока.
    /// </summary>
    /// <param name="amount">Количество восстанавливаемой энергии.</param>
    public void RestoreEnergy(int amount)
    {
        energy += amount;
        energy = Mathf.Clamp(energy, 0, 100);
        Debug.Log("Энергия: " + energy);
    }

    /// <summary>
    /// Восстановление здоровья игрока.
    /// </summary>
    /// <param name="amount">Количество восстанавливаемого здоровья.</param>
    public void RestoreHealth(int amount)
    {
        health += amount;
        health = Mathf.Clamp(health, 0, 100);
        Debug.Log("HP: " + health);
    }

    /// <summary>
    /// Получение урона игроком.
    /// </summary>
    /// <param name="amount">Количество полученного урона.</param>
    public void TakeDamage(int amount)
    {
        health -= amount;
        health = Mathf.Clamp(health, 0, 100);
        Debug.Log("HP: " + health);
    }

    /// <summary>
    /// Метод для броска предмета.
    /// </summary>
    /// <param name="item">Предмет для броска.</param>
    public void ThrowItem(Item item)
    {
        if (item is Ball ball)
        {
            if (ballPrefab != null)
            {
                GameObject thrownBall = ObjectPool.Instance.GetPooledObject(ballPrefab.name);
                if (thrownBall != null)
                {
                    thrownBall.transform.position = transform.position;
                    thrownBall.transform.rotation = Quaternion.identity;
                    thrownBall.SetActive(true);
                    Rigidbody2D rb = thrownBall.GetComponent<Rigidbody2D>();
                    if (rb != null)
                    {
                        rb.AddForce(transform.up * ball.throwForce);
                    }
                }
            }
            else
            {
                Debug.LogWarning("Префаб BallPrefab не назначен в Player.");
            }
        }
    }

    /// <summary>
    /// Метод для выброса предмета на карту.
    /// </summary>
    /// <param name="item">Предмет для выброса.</param>
    /// <param name="position">Позиция выброса.</param>
    public void DropItem(Item item, Vector3 position)
    {
        if (worldItemPrefab != null)
        {
            GameObject droppedItem = ObjectPool.Instance.GetPooledObject(worldItemPrefab.name);
            if (droppedItem != null)
            {
                droppedItem.transform.position = position;
                droppedItem.SetActive(true);
                WorldItem wi = droppedItem.GetComponent<WorldItem>();
                if (wi != null)
                {
                    wi.item = item;
                    wi.amount = 1;
                }
                else
                {
                    Debug.LogWarning("Префаб WorldItemPrefab не содержит компонент WorldItem.");
                }
            }
        }
        else
        {
            Debug.LogWarning("Префаб WorldItemPrefab не назначен в Player.");
        }
    }

    /// <summary>
    /// Метод для ремонта устройства с использованием скотча.
    /// </summary>
    /// <param name="repairAmount">Количество восстанавливаемой прочности.</param>
    public void RepairDevice(int repairAmount)
    {
        // Реализуйте логику ремонта устройства
        Debug.Log($"Устройство отремонтировано на {repairAmount} прочности.");
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Ball"))
        {
            // Возврат мяча в пул после столкновения
            ObjectPool.Instance.ReturnToPool("BallPrefab", collision.gameObject);
        }
    }
}
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;

/// <summary>
/// Класс для сохранения и загрузки инвентаря игрока.
/// </summary>
public class InventorySaveLoad : MonoBehaviour
{
    private string saveFilePath;

    private void Start()
    {
        saveFilePath = Path.Combine(Application.persistentDataPath, "inventory_save.json");
        LoadInventory();
    }

    /// <summary>
    /// Метод сохранения инвентаря в файл.
    /// </summary>
    public void SaveInventory()
    {
        List<SaveInventoryItem> saveItems = new List<SaveInventoryItem>();
        foreach (var inventoryItem in Inventory.Instance.items)
        {
            saveItems.Add(new SaveInventoryItem
            {
                itemName = inventoryItem.item.name,
                amount = inventoryItem.amount
            });
        }

        string json = JsonConvert.SerializeObject(saveItems, Formatting.Indented);
        File.WriteAllText(saveFilePath, json);
        Debug.Log("Инвентарь сохранен.");
    }

    /// <summary>
    /// Метод загрузки инвентаря из файла.
    /// </summary>
    public void LoadInventory()
    {
        if (File.Exists(saveFilePath))
        {
            string json = File.ReadAllText(saveFilePath);
            List<SaveInventoryItem> saveItems = JsonConvert.DeserializeObject<List<SaveInventoryItem>>(json);
            foreach (var saveItem in saveItems)
            {
                Item item = Resources.Load<Item>(saveItem.itemName);
                if (item != null)
                {
                    Inventory.Instance.Add(item, saveItem.amount);
                }
                else
                {
                    Debug.LogWarning($"Предмет с именем {saveItem.itemName} не найден в Resources.");
                }
            }
            Debug.Log("Инвентарь загружен.");
        }
        else
        {
            Debug.Log("Файл сохранения инвентаря не найден.");
        }
    }

    /// <summary>
    /// Класс для сохранения данных предмета.
    /// </summary>
    [System.Serializable]
    public class SaveInventoryItem
    {
        public string itemName; // Название ScriptableObject
        public int amount;
    }

    private void OnApplicationQuit()
    {
        SaveInventory();
    }
}
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Класс для сундуков, из которых можно добывать лут.
/// </summary>
public class Chest : MonoBehaviour
{
    [Header("Луттаблица")]
    public LootTable lootTable;

    [Header("Настройки сундука")]
    public bool isOpen = false;
    public Animator animator; // Аниматор для открытия сундука

    [Header("UI сундука")]
    public GameObject chestUI; // Панель UI сундука
    public Button lootButton;   // Кнопка лута

    private void Start()
    {
        if (lootButton != null)
        {
            lootButton.onClick.AddListener(GenerateLoot);
            chestUI.SetActive(false);
        }
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player") && !isOpen)
        {
            OpenChestUI();
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.CompareTag("Player") && !isOpen)
        {
            CloseChestUI();
        }
    }

    /// <summary>
    /// Метод для открытия UI сундука.
    /// </summary>
    private void OpenChestUI()
    {
        chestUI.SetActive(true);
        if (animator != null)
        {
            animator.SetBool("isOpen", true);
        }
    }

    /// <summary>
    /// Метод для закрытия UI сундука.
    /// </summary>
    private void CloseChestUI()
    {
        chestUI.SetActive(false);
        if (animator != null)
        {
            animator.SetBool("isOpen", false);
        }
    }

    /// <summary>
    /// Метод генерации и добавления лута в инвентарь игрока.
    /// </summary>
    private void GenerateLoot()
    {
        if (lootTable == null || lootTable.lootItems.Count == 0)
        {
            Debug.LogWarning("Луттаблица пустая или не назначена.");
            return;
        }

        // Генерация случайного количества предметов
        int numberOfItems = Random.Range(1, 4); // Например, от 1 до 3 предметов
        for (int i = 0; i < numberOfItems; i++)
        {
            Item randomItem = lootTable.GetRandomItem();
            if (randomItem != null)
            {
                bool added = Inventory.Instance.Add(randomItem);
                if (added)
                {
                    Debug.Log($"Добыт предмет: {randomItem.itemName}");
                }
                else
                {
                    Debug.Log("Не удалось добавить предмет в инвентарь (инвентарь полный).");
                }
            }
        }

        isOpen = true;
        CloseChestUI();
    }
}
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Класс для определения лута в сундуках.
/// </summary>
[System.Serializable]
public class LootTable
{
    [Header("Настройки лута")]
    [Tooltip("Список возможных предметов для лута.")]
    public List<LootItem> lootItems;

    /// <summary>
    /// Метод для получения случайного предмета из луттаблицы.
    /// </summary>
    /// <returns>Выбранный предмет.</returns>
    public Item GetRandomItem()
    {
        if (lootItems.Count == 0)
            return null;

        float totalWeight = 0f;
        foreach (var lootItem in lootItems)
        {
            totalWeight += lootItem.weight;
        }

        float randomValue = Random.Range(0f, totalWeight);
        float cumulativeWeight = 0f;
        foreach (var lootItem in lootItems)
        {
            cumulativeWeight += lootItem.weight;
            if (randomValue <= cumulativeWeight)
            {
                return lootItem.item;
            }
        }

        return null;
    }
}

/// <summary>
/// Класс для представления предмета в луттаблице с весом.
/// </summary>
[System.Serializable]
public class LootItem
{
    public Item item;
    [Range(0f, 1f)]
    public float weight; // Вероятность выпадения (сумма весов может быть любая)
}


using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Класс для отображения подсказок (Tooltips) над предметами.
/// </summary>
public class Tooltip : MonoBehaviour
{
    public static Tooltip Instance;

    [Header("Ссылки на UI элементы")]
    [Tooltip("Панель подсказки.")]
    public GameObject tooltipPanel;

    [Tooltip("Текст подсказки.")]
    public Text tooltipText;

    private RectTransform rectTransform;

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(this);
            return;
        }
        Instance = this;

        rectTransform = tooltipPanel.GetComponent<RectTransform>();
        tooltipPanel.SetActive(false);
    }

    private void Update()
    {
        if (tooltipPanel.activeSelf)
        {
            Vector2 position;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(
                transform as RectTransform, 
                Input.mousePosition, 
                null, 
                out position);
            rectTransform.anchoredPosition = position + new Vector2(10, -10);
        }
    }

    /// <summary>
    /// Метод для отображения подсказки.
    /// </summary>
    /// <param name="text">Текст подсказки.</param>
    public void ShowTooltip(string text)
    {
        tooltipPanel.SetActive(true);
        tooltipText.text = text;
    }

    /// <summary>
    /// Метод для скрытия подсказки.
    /// </summary>
    public void HideTooltip()
    {
        tooltipPanel.SetActive(false);
    }
}
using UnityEngine;

/// <summary>
/// Класс управления UI инвентаря.
/// </summary>
public class InventoryUI : MonoBehaviour
{
    [Header("Ссылки на объекты UI")]
    [Tooltip("Панель интерфейса инвентаря.")]
    public GameObject inventoryUIPanel;

    [Tooltip("Родитель для слотов инвентаря.")]
    public Transform itemsParent;

    [Tooltip("Слот, который перетаскивается.")]
    public InventorySlot draggedSlot;

    private Inventory inventory;
    private InventorySlot[] slots;

    private void Start()
    {
        inventory = Inventory.Instance;
        inventory.onItemChangedCallback += UpdateUI;
        slots = itemsParent.GetComponentsInChildren<InventorySlot>();
        inventoryUIPanel.SetActive(false);
    }

    private void Update()
    {
        // Открытие/закрытие инвентаря по нажатию TAB
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            inventoryUIPanel.SetActive(!inventoryUIPanel.activeSelf);
        }

        // Обработка оставшегося перетаскивания
        if (draggedSlot != null)
        {
            Vector2 mousePosition;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(
                inventoryUIPanel.GetComponent<RectTransform>(), 
                Input.mousePosition, 
                null, 
                out mousePosition);
            draggedSlot.GetComponent<RectTransform>().anchoredPosition = mousePosition;
        }
    }

    /// <summary>
    /// Метод обновления UI инвентаря.
    /// </summary>
    private void UpdateUI()
    {
        for (int i = 0; i < slots.Length; i++)
        {
            if (i < inventory.items.Count)
            {
                slots[i].AddItem(inventory.items[i]);
            }
            else
            {
                slots[i].ClearSlot();
            }
        }
    }

    /// <summary>
    /// Установка текущего перетаскиваемого слота.
    /// </summary>
    /// <param name="slot">Слот, который перетаскивается.</param>
    public void SetDraggedSlot(InventorySlot slot)
    {
        draggedSlot = slot;
    }

    /// <summary>
    /// Метод для обмена предметами между слотами.
    /// </summary>
    /// <param name="source">Источник (перетаскиваемый) слот.</param>
    /// <param name="target">Целевой слот.</param>
    public void SwapSlots(InventorySlot source, InventorySlot target)
    {
        if (source == null || target == null || source == target)
            return;

        source.SwapWith(target);
    }

    /// <summary>
    /// Метод, вызываемый при окончании перетаскивания, для проверки обмена.
    /// </summary>
    /// <param name="targetSlot">Целевой слот.</param>
    public void OnSlotDropped(InventorySlot targetSlot)
    {
        if (draggedSlot != null && targetSlot != null)
        {
            SwapSlots(draggedSlot, targetSlot);
        }
    }
}
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

/// <summary>
/// Класс для отдельного слота инвентаря.
/// Реализует интерфейсы для перетаскивания предметов и подсказок.
/// </summary>
public class InventorySlot : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
    [Header("Элементы слота")]
    [Tooltip("Изображение для иконки предмета.")]
    public Image icon;

    [Tooltip("Текст для отображения количества предметов.")]
    public Text amountText;

    private InventoryItem inventoryItem;

    private InventoryUI inventoryUI;

    private CanvasGroup canvasGroup;
    private RectTransform rectTransform;
    private Canvas canvas;

    [Header("Подсказки")]
    private Tooltip tooltip;

    private void Awake()
    {
        inventoryUI = FindObjectOfType<InventoryUI>();
        canvasGroup = GetComponent<CanvasGroup>();
        rectTransform = GetComponent<RectTransform>();
        canvas = GetComponentInParent<Canvas>();
    }

    private void Start()
    {
        tooltip = Tooltip.Instance;
    }

    /// <summary>
    /// Добавление предмета в слот.
    /// </summary>
    /// <param name="newItem">Предмет для добавления.</param>
    public void AddItem(InventoryItem newItem)
    {
        inventoryItem = newItem;
        icon.sprite = inventoryItem.item.icon;
        icon.enabled = true;
        if (inventoryItem.amount > 1)
        {
            amountText.text = inventoryItem.amount.ToString();
            amountText.enabled = true;
        }
        else
        {
            amountText.text = "";
            amountText.enabled = false;
        }
    }

    /// <summary>
    /// Очистка слота.
    /// </summary>
    public void ClearSlot()
    {
        inventoryItem = null;
        icon.sprite = null;
        icon.enabled = false;
        amountText.text = "";
        amountText.enabled = false;
    }

    /// <summary>
    /// Обработка кликов по слоту.
    /// Левый клик - использование предмета.
    /// Правый клик - выброс предмета.
    /// </summary>
    /// <param name="eventData">Данные о событии.</param>
    public void OnPointerClick(PointerEventData eventData)
    {
        if (inventoryItem == null)
            return;

        Player player = FindObjectOfType<Player>();
        if (player == null)
        {
            Debug.LogWarning("Компонент Player не найден в сцене.");
            return;
        }

        if (eventData.button == PointerEventData.InputButton.Right)
        {
            // Выкинуть один предмет
            player.DropItem(inventoryItem.item, player.transform.position);
            Inventory.Instance.Remove(inventoryItem.item, 1);
        }
        else if (eventData.button == PointerEventData.InputButton.Left)
        {
            // Использовать предмет
            inventoryItem.item.Use(player);
            if (inventoryItem.item.type == ItemType.Consumable || inventoryItem.item.type == ItemType.Misc)
            {
                Inventory.Instance.Remove(inventoryItem.item, 1);
            }
        }
    }

    /// <summary>
    /// Начало перетаскивания предмета.
    /// </summary>
    /// <param name="eventData">Данные о событии.</param>
    public void OnBeginDrag(PointerEventData eventData)
    {
        if (inventoryItem == null)
            return;

        inventoryUI.SetDraggedSlot(this);
        canvasGroup.blocksRaycasts = false;
    }

    /// <summary>
    /// Перетаскивание предмета.
    /// </summary>
    /// <param name="eventData">Данные о событии.</param>
    public void OnDrag(PointerEventData eventData)
    {
        if (inventoryItem == null)
            return;

        rectTransform.anchoredPosition += eventData.delta / canvas.scaleFactor;
    }

    /// <summary>
    /// Завершение перетаскивания предмета.
    /// </summary>
    /// <param name="eventData">Данные о событии.</param>
    public void OnEndDrag(PointerEventData eventData)
    {
        inventoryUI.SetDraggedSlot(null);
        canvasGroup.blocksRaycasts = true;
    }

    /// <summary>
    /// Метод для обмена предметами между слотами.
    /// </summary>
    /// <param name="targetSlot">Целевой слот.</param>
    public void SwapWith(InventorySlot targetSlot)
    {
        if (targetSlot == null)
            return;

        InventoryItem tempItem = targetSlot.inventoryItem;
        targetSlot.AddItem(this.inventoryItem);
        if (tempItem != null)
        {
            this.AddItem(tempItem);
        }
        else
        {
            this.ClearSlot();
        }
    }

    /// <summary>
    /// Получение предмета в слоте.
    /// </summary>
    /// <returns>Предмет в слоте.</returns>
    public InventoryItem GetItem()
    {
        return inventoryItem;
    }

    /// <summary>
    /// Обработка наведения курсора на слот.
    /// </summary>
    /// <param name="eventData">Данные о событии.</param>
    public void OnPointerEnter(PointerEventData eventData)
    {
        if (inventoryItem != null && tooltip != null)
        {
            string tooltipContent = $"{inventoryItem.item.itemName}\n";
            switch (inventoryItem.item.type)
            {
                case ItemType.Consumable:
                    if (inventoryItem.item is Soda soda)
                    {
                        tooltipContent += $"Восстановление энергии: {soda.energyRestore}\nПотеря здоровья: {soda.healthPenalty}";
                    }
                    else if (inventoryItem.item is FirstAidKit kit)
                    {
                        tooltipContent += $"Восстановление здоровья: {kit.healthRestore}";
                    }
                    break;
                case ItemType.Throwable:
                    tooltipContent += "Можно бросить.";
                    break;
                case ItemType.Equipment:
                    tooltipContent += "Снаряжение.";
                    break;
                case ItemType.Misc:
                    if (inventoryItem.item is Notebook notebook)
                    {
                        tooltipContent += $"Содержимое: {notebook.notes}";
                    }
                    else if (inventoryItem.item is Tape tape)
                    {
                        tooltipContent += $"Используется для ремонта: {tape.repairAmount} прочности";
                    }
                    break;
            }
            tooltip.ShowTooltip(tooltipContent);
        }
    }

    /// <summary>
    /// Обработка ухода курсора с слота.
    /// </summary>
    /// <param name="eventData">Данные о событии.</param>
    public void OnPointerExit(PointerEventData eventData)
    {
        if (tooltip != null)
        {
            tooltip.HideTooltip();
        }
    }
}
using UnityEngine;

/// <summary>
/// Класс для представления предмета в инвентаре с количеством.
/// </summary>
[System.Serializable]
public class InventoryItem
{
    public Item item;
    public int amount;

    public InventoryItem(Item item, int amount)
    {
        this.item = item;
        this.amount = amount;
    }
}

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Класс управления инвентарем игрока.
/// Реализует Singleton паттерн.
/// </summary>
public class Inventory : MonoBehaviour
{
    // Singleton экземпляр инвентаря
    public static Inventory Instance { get; private set; }

    [Header("Настройки инвентаря")]
    [Tooltip("Максимальное количество слотов в инвентаре.")]
    public int space = 20; // Изменено для большего функционала

    [Header("Параметры предметов")]
    [Tooltip("Максимальное количество одного предмета в стеке.")]
    public int maxStackSize = 99;

    [HideInInspector]
    public List<InventoryItem> items = new List<InventoryItem>();

    /// <summary>
    /// Делегат для события изменения инвентаря.
    /// </summary>
    public delegate void OnItemChanged();
    public OnItemChanged onItemChangedCallback;

    private void Awake()
    {
        // Реализация Singleton паттерна
        if (Instance != null && Instance != this)
        {
            Debug.LogWarning("Обнаружено несколько экземпляров Inventory! Оставляем первый.");
            Destroy(this);
            return;
        }
        Instance = this;
    }

    /// <summary>
    /// Метод добавления предмета в инвентарь.
    /// </summary>
    /// <param name="item">Предмет для добавления.</param>
    /// <param name="amount">Количество предметов.</param>
    /// <returns>Успешно ли добавление.</returns>
    public bool Add(Item item, int amount = 1)
    {
        if (item == null)
            return false;

        // Проверка наличия предмета в стеке
        InventoryItem existingItem = items.Find(i => i.item == item);
        if (existingItem != null && (item.type == ItemType.Consumable || item.type == ItemType.Misc))
        {
            if (existingItem.amount + amount <= maxStackSize)
            {
                existingItem.amount += amount;
                onItemChangedCallback?.Invoke();
                return true;
            }
            else
            {
                int remaining = maxStackSize - existingItem.amount;
                if (remaining > 0)
                {
                    existingItem.amount += remaining;
                    amount -= remaining;
                }
            }
        }

        // Добавление нового предмета, если есть место
        while (amount > 0 && items.Count < space)
        {
            int addAmount = amount > maxStackSize ? maxStackSize : amount;
            items.Add(new InventoryItem(item, addAmount));
            amount -= addAmount;
        }

        if (amount > 0)
        {
            Debug.Log("Инвентарь не может вместить все предметы.");
            onItemChangedCallback?.Invoke();
            return false;
        }

        onItemChangedCallback?.Invoke();
        return true;
    }

    /// <summary>
    /// Метод удаления предмета из инвентаря.
    /// </summary>
    /// <param name="item">Предмет для удаления.</param>
    /// <param name="amount">Количество предметов для удаления.</param>
    public void Remove(Item item, int amount = 1)
    {
        InventoryItem existingItem = items.Find(i => i.item == item);
        if (existingItem != null)
        {
            existingItem.amount -= amount;
            if (existingItem.amount <= 0)
            {
                items.Remove(existingItem);
            }
            onItemChangedCallback?.Invoke();
        }
    }
}

Улучши ещё сильнее + на поднятие каждого предмета надо отвечать, также каждый вопрос я могу писать + к нему ответ и варианты ответов (без кода чтоб я мог) + другие аспекты + исправь ошибки: Cannot resolve symbol 'Newtonsoft' Cannot resolve symbol 'UIManager' Также напиши полный код + инструкции