// ExpeditionLocation.cs - хранит данные о месте для экспедиции
using UnityEngine;

[System.Serializable]
public class ExpeditionLocation
{
    public string name;
    public string description;
    public int populationCost;
    public int foodCost;
    public int weaponsCost;
    public int duration; // в днях
    public float successChance;
    public Reward reward;

    // Конструктор для ручного создания
    public ExpeditionLocation(string name, string description, int populationCost, int foodCost, int weaponsCost, int duration, float successChance, Reward reward)
    {
        this.name = name;
        this.description = description;
        this.populationCost = populationCost;
        this.foodCost = foodCost;
        this.weaponsCost = weaponsCost;
        this.duration = duration;
        this.successChance = successChance;
        this.reward = reward;
    }
}


// ExpeditionManager.cs - логика отправки экспедиций и мест для экспедиций
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class ExpeditionManager : MonoBehaviour
{
    public static ExpeditionManager Instance { get; private set; }

    public GameObject expeditionPanel;
    public GameObject expeditionConditionsPanel;
    public GameObject expInfoPanel;
    public Transform expeditionListContent;
    public GameObject expeditionItemPrefab;

    public Button ExpeditionsButton; // Кнопка открытия панели экспедиций
    public Button sendExpeditionButton; // Кнопка отправки экспедиции
    public Button closeConditionsButton; // Кнопка закрытия панели условий
    public Button okResultButton; // Кнопка "ОК" на панели результатов

    public List<ExpeditionLocation> locations = new List<ExpeditionLocation>();
    private List<Expedition> activeExpeditions = new List<Expedition>();

    private ExpeditionLocation currentLocation; // Текущее место экспедиции
    private Scrollbar currentExpeditionProgress; // Scrollbar для текущей экспедиции

    public List<ExpeditionLocation> availableLocations = new List<ExpeditionLocation>(); // Доступные локации
    public List<ExpeditionLocation> exploredLocations = new List<ExpeditionLocation>(); // Исследованные локации

    void Start()
    {
        CreateExpeditionLocations(); // вызов метода

        ExpeditionsButton.onClick.AddListener(ToggleExpeditionPanel);
        UpdateExpeditionsButton();
    }

    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject);
        }
    }

    public void AddNewLocation(ExpeditionLocation location)
    {
        availableLocations.Add(location);
        DisplayLocations(location); // !!! Вызываем с найденной локацией
    }

    // Метод для ручного создания локаций
    private void CreateExpeditionLocations()
    {
        availableLocations.Add(new ExpeditionLocation(
            "Заброшенный склад",
            "Здесь могут быть полезные ресурсы, но будьте осторожны.",
            10, 20, 5, 2, 0.7f,
            new Reward(buildingMaterials: 20, food: 30)
        ));

        availableLocations.Add(new ExpeditionLocation(
            "Руины города",
            "Когда-то здесь кипела жизнь, теперь - лишь призраки прошлого.",
            15, 30, 10, 3, 0.6f,
            new Reward(gold: 50, weapons: 10)
        ));

        availableLocations.Add(new ExpeditionLocation(
            "Заброшенный склад ПивТех",
            "Здесь склад старого алкоголя.",
            10, 20, 5, 2, 0.7f,
            new Reward(buildingMaterials: 20, food: 30)
));

        availableLocations.Add(new ExpeditionLocation(
            "Дача Мэра",
            "Когда-то здесь кипела жизнь, теперь мэр боится тут появлятся.",
            15, 30, 10, 3, 0.6f,
            new Reward(gold: 50, weapons: 10)
        ));

        // ... тут место для ещё локаций ...
    }

    public void DisplayLocations(ExpeditionLocation location)
    {
        expeditionPanel.SetActive(true);

        // Отображение найденной локации
        GameObject listItem = Instantiate(expeditionItemPrefab, expeditionListContent);

        TextMeshProUGUI locationNameText = listItem.transform.Find("LocationName").GetComponent<TextMeshProUGUI>();
        TextMeshProUGUI locationDescriptionText = listItem.transform.Find("LocationDescription").GetComponent<TextMeshProUGUI>();
        Button detailsButton = listItem.transform.Find("DetailsButton").GetComponent<Button>();
        Scrollbar expeditionProgress = listItem.transform.Find("ExpeditionProgress").GetComponent<Scrollbar>();

        locationNameText.text = location.name;
        locationDescriptionText.text = location.description;

        // Назначаем функцию ShowExpeditionConditions для кнопки DetailsButton
        detailsButton.onClick.AddListener(() => ShowExpeditionConditions(location, expeditionProgress));

        // Скрываем Scrollbar, пока экспедиция не запущена
        expeditionProgress.gameObject.SetActive(false);
    }

    public void ShowExpeditionConditions(ExpeditionLocation location, Scrollbar expeditionProgress)
    {
        expeditionConditionsPanel.SetActive(true);

        // СНАЧАЛА НАХОДИМ ВСЕ КОМПОНЕНТЫ:
        TextMeshProUGUI locationNameText = expeditionConditionsPanel.transform.Find("LocationNameText").GetComponent<TextMeshProUGUI>();
        TextMeshProUGUI locationDescriptionText = expeditionConditionsPanel.transform.Find("LocationDescriptionText").GetComponent<TextMeshProUGUI>();
        TextMeshProUGUI populationCostText = expeditionConditionsPanel.transform.Find("PopulationCostText").GetComponent<TextMeshProUGUI>();
        TextMeshProUGUI foodCostText = expeditionConditionsPanel.transform.Find("FoodCostText").GetComponent<TextMeshProUGUI>();
        TextMeshProUGUI weaponsCostText = expeditionConditionsPanel.transform.Find("WeaponsCostText").GetComponent<TextMeshProUGUI>();
        TextMeshProUGUI durationText = expeditionConditionsPanel.transform.Find("DurationText").GetComponent<TextMeshProUGUI>();

        // ПОТОМ ИСПОЛЬЗУЕМ ИХ:
        locationNameText.text = location.name;
        locationDescriptionText.text = location.description;
        populationCostText.text = $"Люди: {location.populationCost}";
        foodCostText.text = $"Еда: {location.foodCost}";
        weaponsCostText.text = $"Оружие: {location.weaponsCost}";
        durationText.text = $"Время: {location.duration} дней";

        // Поиск и очистка старых слушателей кнопки "Send"
        sendExpeditionButton = expeditionConditionsPanel.transform.Find("SendButton").GetComponent<Button>();
        sendExpeditionButton.onClick.RemoveAllListeners();
        sendExpeditionButton.onClick.AddListener(SendExpedition);

        // Поиск и очистка старых слушателей кнопки "Close"
        closeConditionsButton = expeditionConditionsPanel.transform.Find("CloseButton").GetComponent<Button>();
        closeConditionsButton.onClick.RemoveAllListeners();
        closeConditionsButton.onClick.AddListener(CloseConditionsPanel);

        // Сохраняем текущие location и expeditionProgress
        currentLocation = location;
        currentExpeditionProgress = expeditionProgress;
    }

    private void SendExpedition()
    {
        if (currentLocation != null && currentExpeditionProgress != null)
        {
            // Дополнительная проверка перед запуском экспедиции
            if (activeExpeditions.Exists(e => e.location == currentLocation) || exploredLocations.Contains(currentLocation))
            {
                NotificationManager.Instance.ShowNotification(" (SendExpedition) Экспедиция в это место уже отправлена!", NotificationType.Bad);
                return;
            }

            StartExpedition(currentLocation, currentExpeditionProgress);
            expeditionConditionsPanel.SetActive(false);
        }
    }

    private void StartExpedition(ExpeditionLocation location, Scrollbar expeditionProgress)
    {
        // Проверка на наличие активной или уже исследованной локации
        if (activeExpeditions.Exists(e => e.location == location) || exploredLocations.Contains(location))
        {
            NotificationManager.Instance.ShowNotification("Экспедиция в это место уже отправлена!", NotificationType.Bad);
            return;
        }

        // Проверка ресурсов
        if (GameManager.Instance.population >= location.populationCost &&
            GameManager.Instance.resources.food >= location.foodCost &&
            GameManager.Instance.resources.weapons >= location.weaponsCost)
        {
            // Вычитаем ресурсы 
            GameManager.Instance.population -= location.populationCost;
            GameManager.Instance.resources.food -= location.foodCost;
            GameManager.Instance.resources.weapons -= location.weaponsCost;

            // Создаем экспедицию и добавляем в список активных
            Expedition expedition = new Expedition(location, expeditionProgress);
            activeExpeditions.Add(expedition);

            // Запускаем корутину экспедиции
            StartCoroutine(RunExpedition(expedition));

            // Обновляем UI ресурсов и населения сразу после отправки экспедиции
            GameManager.Instance.resources.UpdateResourceUI();
            GameManager.Instance.UpdatePopulationText();

            // Добавляем локацию в список exploredLocations после отправки экспедиции:
            exploredLocations.Add(location);

            // Добавляем запись в историю
            HistoryManager.Instance.AddHistoryEntry(
                $"Отправлена экспедиция в {location.name}. " +
                $"Затраты: {location.populationCost} человек, " +
                $"{location.foodCost} еды, {location.weaponsCost} оружия."
            );
        }
        else
        {
            // Недостаточно ресурсов
            NotificationManager.Instance.ShowNotification("Недостаточно ресурсов для отправки экспедиции!", NotificationType.Bad);
        }
    }

    private IEnumerator RunExpedition(Expedition expedition)
    {
        // Активируем Scrollbar и устанавливаем значение 0
        expedition.progressBar.gameObject.SetActive(true);
        expedition.progressBar.size = 0f;

        // Вычисляем общее время экспедиции в секундах
        float totalExpeditionTime = expedition.location.duration * Calendar.Instance.dayLength;

        float elapsedTime = 0f;

        // Цикл работает, пока не пройдет время экспедиции И пока экспедиция есть в списке activeExpeditions
        while (elapsedTime < totalExpeditionTime && activeExpeditions.Contains(expedition))
        {
            // Обновляем Scrollbar
            expedition.progressBar.size = elapsedTime / totalExpeditionTime;

            elapsedTime += Time.deltaTime;
            yield return null;
        }

        // Если экспедиция была отменена (удалена из activeExpeditions), выходим из корутины
        if (!activeExpeditions.Contains(expedition))
        {
            exploredLocations.Remove(expedition.location); // <<< Добавлено
            yield break;
        }

        // Экспедиция завершена, удаляем из списка активных
        activeExpeditions.Remove(expedition);

        // Определяем результат экспедиции
        bool success = Random.value < expedition.location.successChance;
        ShowExpeditionResult(expedition.location, success);

        // Скрываем Scrollbar
        expedition.progressBar.gameObject.SetActive(false);
    }

    private void ShowExpeditionResult(ExpeditionLocation location, bool success)
    {
        expInfoPanel.SetActive(true);

        TextMeshProUGUI expResultText = expInfoPanel.transform.Find("ExpResultText").GetComponent<TextMeshProUGUI>();

        if (success)
        {
            // Добавляем награду к ресурсам игрока:
            GameManager.Instance.resources.buildingMaterials += location.reward.buildingMaterials;
            GameManager.Instance.resources.food += location.reward.food;
            GameManager.Instance.resources.gold += location.reward.gold;
            GameManager.Instance.resources.weapons += location.reward.weapons;

            // Формируем текст с результатами:
            expResultText.text = $"Экспедиция в {location.name} завершилась успешно!\n" +
                                  $"В поселение было принесено:\n" +
                                  $"{location.reward.buildingMaterials} стройматериалов\n" +
                                  $"{location.reward.food} еды\n" +
                                  $"{location.reward.gold} монет\n" +
                                  $"{location.reward.weapons} оружия";

            // Добавляем запись в историю:
            HistoryManager.Instance.AddHistoryEntry(
                $"Экспедиция в {location.name} завершилась успешно! " +
                $"Добыча: {location.reward.buildingMaterials} стройматериалов, " +
                $"{location.reward.food} еды, {location.reward.gold} монет, " +
                $"{location.reward.weapons} оружия."
            );
        }
        else
        {
            // Формируем текст с результатами:
            expResultText.text = $"Экспедиция в {location.name} провалена!\n";

            // Добавляем запись в историю:
            HistoryManager.Instance.AddHistoryEntry($"Экспедиция в {location.name} провалена!");
        }

        // Переносим локацию в список exploredLocations
        availableLocations.Remove(location);
        exploredLocations.Add(location);

        // Обновляем UI ресурсов и населения
        GameManager.Instance.resources.UpdateResourceUI();
        GameManager.Instance.UpdatePopulationText();
        UpdateExpeditionsButton();

        // Находим кнопку и назначаем функцию:
        okResultButton = expInfoPanel.transform.Find("OKButton").GetComponent<Button>();
        okResultButton.onClick.AddListener(CloseResultPanel);
    }

    private void CloseConditionsPanel()
    {
        expeditionConditionsPanel.SetActive(false);
        currentLocation = null;
        currentExpeditionProgress = null;
    }

    public void CloseResultPanel()
    {
        expInfoPanel.SetActive(false);
    }
    public void ToggleExpeditionPanel()
    {
        expeditionPanel.SetActive(!expeditionPanel.activeSelf);
        UpdateExpeditionsButton();
    }

    // Класс для хранения данных об активной экспедиции
    private class Expedition
    {
        public ExpeditionLocation location;
        public Scrollbar progressBar;

        public Expedition(ExpeditionLocation location, Scrollbar progressBar)
        {
            this.location = location;
            this.progressBar = progressBar;
        }
    }

    private void UpdateExpeditionsButton()
    {
        ExpeditionsButton.interactable = availableLocations.Count > 0;
    }
}

фрагмент из другого скрипта с логикой поиска самих мест для экспедиций.

   private IEnumerator FindSettlement(float successChance, string[] failMessages)
   {
       float searchDuration = 5f; //  длительность поиска
       float timer = 0f;
       while (timer < searchDuration)
       {
           timer += Time.deltaTime;
           progressBar.fillAmount = timer / searchDuration; // плавное заполнение
           yield return null;
       }

       // После завершения поиска - рандомный результат
       if (disableSearchFails || Random.value < successChance)
       {
           Settlement newSettlement = GenerateSettlement();

           if (Random.value < 0.8f)
           {
               // Создаем список неисследованных локаций
               List<ExpeditionLocation> unexploredLocations = ExpeditionManager.Instance.availableLocations.Except(ExpeditionManager.Instance.exploredLocations).ToList();

               // Проверяем, есть ли неисследованные локации
               if (unexploredLocations.Count > 0)
               {
                   // Выбираем случайную локацию из неисследованных
                   ExpeditionLocation newLocation = unexploredLocations[Random.Range(0, unexploredLocations.Count)];

                   ExpeditionManager.Instance.AddNewLocation(newLocation);
                   findSettlementText.text = "Мы нашли что-то интересное!";
                   NotificationManager.Instance.ShowNotification("Вы нашли место для экспедиций!", NotificationType.Info);

                   // Вызываем DisplayLocations() здесь
                   //ExpeditionManager.Instance.DisplayLocations(newLocation);
               }
               else
               {
                   // Сообщение о том, что все локации исследованы
                   findSettlementText.text = "Все известные места для экспедиций уже исследованы.";
               }
           }

           // Проверяем, есть ли уже поселение с таким именем
           if (settlements.Any(s => s.name == newSettlement.name))
           {
               findSettlementText.text = "Поисковики долго блуждали и вышли на уже знакомое поселение.";
           }
           else
           {
               findSettlementText.text = "Мы нашли поселение!";
               settlements.Add(newSettlement);
               OpenDiplomacyPanel(newSettlement);
           }
       }
       else
       {
           // Выбираем случайное сообщение о провале:
           string failMessage = failMessages[Random.Range(0, failMessages.Length)];
           findSettlementText.text = failMessage;

           // Применяем последствия провала в зависимости от сообщения:
           if (failMessage.Contains("погиб"))
           {
               gameManager.population -= 6;
               gameManager.happiness -= 3;
           }
           else if (failMessage.Contains("счастье"))
           {
               // Извлекаем число из строки (например, "-3" из " -3 счастья населения")
               int happinessChange = int.Parse(failMessage.Substring(failMessage.IndexOf('-') + 1, 1));
               gameManager.happiness += happinessChange; // Обратите внимание на "+" - мы уменьшаем счастье
           }

           gameManager.UpdatePopulationText();
           gameManager.UpdateHappinessText();
       }

       diplomacyButton.interactable = true; // Разблокируем кнопку после поиска
       yield return new WaitForSeconds(2f); // Пауза для просмотра результата
       findSettlementPanel.SetActive(false); // Скрываем панель поиска
   }

Это код моей игры на движке unity. Проблема заключается в том, что в список НАЙДЕНЫХ МЕСТ добавляются МЕСТА, КОТОРЫЕ УЖЕ НАЙДЕНЫ И ЕСТЬ В СПИСКЕ (выводятся в ExpeditionPanel) - так быть не должно - это баг! Когда все возможные места были найдены, нужно больше их не добавлять! РАЗРАБОТАЙ ЭТО И РАССКАЖИ КАК СДЕЛАТЬ МНЕ