Ejemplo n.º 1
0
 private static bool IsHasTransitionToNextLevel(ISectorNode sectorNode, ISectorSubScheme x)
 {
     return(sectorNode.SectorScheme.TransSectorSids
            .Where(x => x != null).Select(x => x !)
            .Select(trans => trans.SectorLevelSid)
            .Contains(x.Sid));
 }
Ejemplo n.º 2
0
        /// <inheritdoc />
        /// <summary>
        /// Создать сундуки в секторе.
        /// </summary>
        public void CreateChests(ISector sector, ISectorSubScheme sectorSubScheme, IEnumerable <MapRegion> regions)
        {
            if (sector is null)
            {
                throw new ArgumentNullException(nameof(sector));
            }

            if (sectorSubScheme is null)
            {
                throw new ArgumentNullException(nameof(sectorSubScheme));
            }

            if (regions is null)
            {
                throw new ArgumentNullException(nameof(regions));
            }

            var trashDropTables    = GetTrashDropTables(sectorSubScheme);
            var treasuresDropTable = GetTreasuresDropTable();
            var chestCounter       = sectorSubScheme.TotalChestCount;

            //TODO В схемах хранить уже приведённое значение пропорции.
            var countChestRatioNormal = 1f / sectorSubScheme.RegionChestCountRatio;

            foreach (var region in regions)
            {
                var maxChestCountRaw = region.Nodes.Length * countChestRatioNormal;
                var maxChestCount    = (int)Math.Max(maxChestCountRaw, 1);

                CreateChestsForRegion(region, maxChestCount, sector, trashDropTables, treasuresDropTable,
                                      ref chestCounter);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Получение доступных схем моснтров на основе указанной редкости монстра.
        /// </summary>
        /// <param name="sectorScheme"> Настройки генерации монстров. </param>
        /// <param name="currentRarity"> Целевой уровень редкости монстра. </param>
        /// <returns> Возвращает набор строк, являющихся идентификаторами схем монстров. </returns>
        private static IEnumerable <string> GetAvailableSchemeSids(
            ISectorSubScheme sectorScheme,
            int currentRarity)
        {
            IEnumerable <string> availableSchemeSids;

            switch (currentRarity)
            {
            case 0:
                availableSchemeSids = sectorScheme.RegularMonsterSids;
                break;

            case 1:
                availableSchemeSids = sectorScheme.RareMonsterSids ??
                                      sectorScheme.RegularMonsterSids;
                break;

            case 2:
                availableSchemeSids = sectorScheme.ChampionMonsterSids ??
                                      sectorScheme.RareMonsterSids ??
                                      sectorScheme.RegularMonsterSids;
                break;

            default:
                throw new InvalidOperationException();
            }

            if (availableSchemeSids == null)
            {
                throw new InvalidOperationException("Не удалось выбрать доступные схемы для монстров.");
            }

            return(availableSchemeSids);
        }
Ejemplo n.º 4
0
        private void CreateMonstersForRegion(ISector sector, ISectorSubScheme sectorScheme,
                                             List <IActor> resultMonsterActors, MapRegion region, int[] rarityCounter)
        {
            var regionNodes           = region.Nodes.OfType <HexNode>();
            var staticObjectsNodes    = sector.StaticObjectManager.Items.Select(x => x.Node);
            var nodesWithTransitions  = sector.Map.Transitions.Keys.ToArray();
            var availableMonsterNodes = regionNodes.Except(staticObjectsNodes).Except(nodesWithTransitions);

            var freeNodes = new List <IGraphNode>(availableMonsterNodes);

            var monsterCount = _generatorRandomSource.RollRegionCount(
                sectorScheme.MinRegionMonsterCount,
                sectorScheme.RegionMonsterCount);

            for (var i = 0; i < monsterCount; i++)
            {
                // если в комнате все места заняты
                if (!freeNodes.Any())
                {
                    break;
                }

                var rollIndex   = _generatorRandomSource.RollNodeIndex(freeNodes.Count);
                var monsterNode = freeNodes[rollIndex];

                var monster = RollRarityAndCreateMonster(sector, sectorScheme, monsterNode, rarityCounter);

                freeNodes.Remove(monster.Node);
                resultMonsterActors.Add(monster);
            }
        }
Ejemplo n.º 5
0
 public StaticObjectGenerationContext(ISector sector, ISectorSubScheme scheme,
                                      IResourceDepositData resourceDepositData)
 {
     Sector = sector ?? throw new ArgumentNullException(nameof(sector));
     Scheme = scheme ?? throw new ArgumentNullException(nameof(scheme));
     ResourceDepositData = resourceDepositData ?? throw new ArgumentNullException(nameof(resourceDepositData));
 }
Ejemplo n.º 6
0
        /// <summary>Создаёт монстров в секторе по указанной схеме.</summary>
        /// <param name="sector">Целевой сектор.</param>
        /// <param name="monsterRegions">Регионы сектора, где могут быть монстры.</param>
        /// <param name="sectorScheme">Схема сектора. Отсюда берутся параметры генерации монстров.</param>
        public void CreateMonsters(ISector sector,
                                   IEnumerable <MapRegion> monsterRegions,
                                   ISectorSubScheme sectorScheme)
        {
            if (sector is null)
            {
                throw new ArgumentNullException(nameof(sector));
            }

            if (monsterRegions is null)
            {
                throw new ArgumentNullException(nameof(monsterRegions));
            }

            if (sectorScheme is null)
            {
                throw new ArgumentNullException(nameof(sectorScheme));
            }

            var resultMonsterActors = new List <IActor>();
            var rarityCounter       = new int[3];

            foreach (var region in monsterRegions)
            {
                CreateMonstersForRegion(sector, sectorScheme, resultMonsterActors, region, rarityCounter);
            }

            // Инфицируем монстров, если в секторе есть болезни.
            RollInfections(sector, resultMonsterActors);
        }
Ejemplo n.º 7
0
        private static IEnumerable <RoomTransition> CreateTransitions(ISectorSubScheme sectorScheme)
        {
            if (sectorScheme.TransSectorSids == null)
            {
                return(new[] { RoomTransition.CreateGlobalExit() });
            }

            return(sectorScheme.TransSectorSids.Select(sid => new RoomTransition(sid)));
        }
Ejemplo n.º 8
0
 private static IEnumerable <string?>?GetMonsterSidsByRarity(ISectorSubScheme sectorScheme, int currentRarity)
 {
     return(currentRarity switch
     {
         0 => sectorScheme.RegularMonsterSids,
         1 => sectorScheme.RareMonsterSids ?? sectorScheme.RegularMonsterSids,
         2 => sectorScheme.ChampionMonsterSids ??
         sectorScheme.RareMonsterSids ?? sectorScheme.RegularMonsterSids,
         _ => throw new InvalidOperationException()
     });
Ejemplo n.º 9
0
        private async Task GenerateStaticObjectsAsync(
            ISector sector,
            ISectorSubScheme sectorScheme,
            ISectorNode sectorNode)
        {
            var resourceDepositData = _resourceMaterializationMap.GetDepositData(sectorNode);
            var context             = new StaticObjectGenerationContext(sector, sectorScheme, resourceDepositData);

            await _staticObstaclesGenerator.CreateAsync(context).ConfigureAwait(false);
        }
Ejemplo n.º 10
0
        /// <summary>Создаёт монстров в секторе по указанной схеме.</summary>
        /// <param name="sector">Целевой сектор.</param>
        /// <param name="monsterPlayer">Бот, управляющий монстрами. По сути, команда монстров.</param>
        /// <param name="monsterRegions">Регионы сектора, где могут быть монстры.</param>
        /// <param name="sectorScheme">Схема сектора. Отсюда берутся параметры генерации монстров.</param>
        public void CreateMonsters(ISector sector,
                                   IBotPlayer monsterPlayer,
                                   IEnumerable <MapRegion> monsterRegions,
                                   ISectorSubScheme sectorScheme)
        {
            var rarityCounter    = new int[3];
            var rarityMaxCounter = new[] { -1, 10, 1 };

            foreach (var region in monsterRegions)
            {
                var freeNodes = new List <IMapNode>(region.Nodes);

                var monsterCount = _generatorRandomSource.RollRegionCount(sectorScheme.RegionMonsterCount);

                for (int i = 0; i < monsterCount; i++)
                {
                    // если в комнате все места заняты
                    if (!freeNodes.Any())
                    {
                        break;
                    }

                    var currentRarity       = GetMonsterRarity(rarityCounter, rarityMaxCounter);
                    var availableSchemeSids = GetAvailableSchemeSids(sectorScheme, currentRarity);

                    var availableMonsterSchemes = availableSchemeSids.Select(x => _schemeService.GetScheme <IMonsterScheme>(x));

                    var monsterScheme = _generatorRandomSource.RollMonsterScheme(availableMonsterSchemes);

                    // первый монстр ходит по маршруту
                    // остальные бродят произвольно
                    if (i == 0)
                    {
                        // генерируем маршрут обхода
                        var startPatrolNode = region.Nodes.First();
                        var endPatrolNode   = region.Nodes.Last();

                        // генерируем моснтра
                        var patrolRoute = new PatrolRoute(startPatrolNode, endPatrolNode);
                        var monster     = CreateMonster(monsterScheme, startPatrolNode, monsterPlayer);
                        sector.PatrolRoutes[monster] = patrolRoute;

                        freeNodes.Remove(monster.Node);
                    }
                    else
                    {
                        var rollIndex   = _generatorRandomSource.RollNodeIndex(freeNodes.Count);
                        var monsterNode = freeNodes[rollIndex];
                        var monster     = CreateMonster(monsterScheme, monsterNode, monsterPlayer);

                        freeNodes.Remove(monster.Node);
                    }
                }
            }
        }
Ejemplo n.º 11
0
        public void BindSchemeInfo(IBiome biom, ISectorSubScheme sectorScheme)
        {
            if (State != SectorNodeState.SchemeUnknown)
            {
                throw new InvalidOperationException("Неверное состояние узла.");
            }

            Biome        = biom ?? throw new ArgumentNullException(nameof(biom));
            SectorScheme = sectorScheme ?? throw new ArgumentNullException(nameof(sectorScheme));
            State        = SectorNodeState.SchemeKnown;
        }
Ejemplo n.º 12
0
        private IDropTableScheme[] GetTrashDropTables(ISectorSubScheme sectorSubScheme)
        {
            var dropTables = new List <IDropTableScheme>();

            foreach (var chestDropSid in sectorSubScheme.ChestDropTableSids)
            {
                var dropTable = _schemeService.GetScheme <IDropTableScheme>(chestDropSid);
                dropTables.Add(dropTable);
            }

            return(dropTables.ToArray());
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Получение доступных схем моснтров на основе указанной редкости монстра.
        /// </summary>
        /// <param name="sectorScheme"> Настройки генерации монстров. </param>
        /// <param name="currentRarity"> Целевой уровень редкости монстра. </param>
        /// <returns> Возвращает набор строк, являющихся идентификаторами схем монстров. </returns>
        private static IEnumerable <string> GetAvailableSchemeSids(
            ISectorSubScheme sectorScheme,
            int currentRarity)
        {
            var availableSchemeSids = GetMonsterSidsByRarity(sectorScheme, currentRarity);

            if (availableSchemeSids is null)
            {
                throw new InvalidOperationException("Не удалось выбрать доступные схемы для монстров.");
            }

            return(availableSchemeSids.Where(x => x != null).Select(x => x !).ToArray());
        }
        public async Task <ISector> GenerateDungeonAsync(ISectorSubScheme sectorScheme)
        {
            var map = await _mapFactory.CreateAsync(sectorScheme);

            var sector = new Sector(map,
                                    _actorManager,
                                    _propContainerManager,
                                    _dropResolver,
                                    _schemeService,
                                    _equipmentDurableService);

            return(sector);
        }
Ejemplo n.º 15
0
        private async Task CreateAndAddSectorByScheme(IBiome biome, ISectorSubScheme startSectorScheme)
        {
            var sectorNode = new SectorNode(biome, startSectorScheme);

            // Важно генерировать соседние узлы до начала генерации сектора,
            // чтобы знать переходы из сектора.

            biome.AddNode(sectorNode);

            CreateNextSectorNodes(sectorNode, biome);

            var sector = await _sectorGenerator.GenerateAsync(sectorNode).ConfigureAwait(false);

            sectorNode.MaterializeSector(sector);
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Создать сундуки в секторе.
        /// </summary>
        /// <param name="map">Карта сектора. Нужна для определения доступного места для сундука.</param>
        /// <param name="sectorSubScheme">Схема сектора. По сути - настройки для размещения сундуков.</param>
        /// <param name="regions">Регионы, в которых возможно размещение сундуков.</param>
        public void CreateChests(ISectorMap map, ISectorSubScheme sectorSubScheme, IEnumerable <MapRegion> regions)
        {
            var dropTables   = GetDropTables(sectorSubScheme);
            var chestCounter = sectorSubScheme.TotalChestCount;

            //TODO В схемах хранить уже приведённое значение пропорции.
            var countChestRatioNormal = 1f / sectorSubScheme.RegionChestCountRatio;

            foreach (var region in regions)
            {
                var maxChestCountRaw = region.Nodes.Count() * countChestRatioNormal;
                var maxChestCount    = (int)Math.Max(maxChestCountRaw, 1);
                var rolledCount      = _chestGeneratorRandomSource.RollChestCount(maxChestCount);

                var availableNodes = from node in region.Nodes
                                     where !map.Transitions.Keys.Contains(node)
                                     select node;
                var openNodes = new List <IMapNode>(availableNodes);
                for (var i = 0; i < rolledCount; i++)
                {
                    // Выбрать из коллекции доступных узлов
                    var rollIndex     = _chestGeneratorRandomSource.RollNodeIndex(openNodes.Count);
                    var containerNode = MapRegionHelper.FindNonBlockedNode(openNodes[rollIndex], map, openNodes);
                    if (containerNode == null)
                    {
                        // в этом случае будет сгенерировано на один сундук меньше.
                        // узел, с которого не удаётся найти подходящий узел, удаляем,
                        // чтобы больше его не анализировать, т.к. всё равно будет такой же исход.
                        openNodes.Remove(openNodes[rollIndex]);
                        continue;
                    }

                    openNodes.Remove(containerNode);
                    var container = new DropTablePropChest(containerNode,
                                                           dropTables,
                                                           _dropResolver);
                    _propContainerManager.Add(container);

                    chestCounter--;

                    if (chestCounter <= 0)
                    {
                        // лимит сундуков в секторе исчерпан.
                        break;
                    }
                }
            }
        }
Ejemplo n.º 17
0
        private IActor RollRarityAndCreateMonster(ISector sector, ISectorSubScheme sectorScheme, IGraphNode monsterNode,
                                                  int[] rarityCounter)
        {
            var rarityMaxCounter = new[] { -1, 10, 1 };

            var currentRarity       = GetMonsterRarity(rarityCounter, rarityMaxCounter);
            var availableSchemeSids = GetAvailableSchemeSids(sectorScheme, currentRarity);

            var availableMonsterSchemes = availableSchemeSids.Select(x => _schemeService.GetScheme <IMonsterScheme>(x));

            var monsterScheme = _generatorRandomSource.RollMonsterScheme(availableMonsterSchemes);

            var monster = CreateMonster(sector.ActorManager, monsterScheme, monsterNode, _actorTaskSource);

            return(monster);
        }
        private static ISectorNode CreateSectorNode(ISectorSubScheme sectorScheme)
        {
            var biomeMock = new Mock <IBiome>();

            biomeMock.Setup(x => x.GetNext(It.IsAny <ISectorNode>())).Returns(Array.Empty <ISectorNode>());
            var biome = biomeMock.Object;

            var sectorNodeMock = new Mock <ISectorNode>();

            sectorNodeMock.SetupGet(x => x.SectorScheme).Returns(sectorScheme);
            sectorNodeMock.SetupGet(x => x.Biome).Returns(biome);
            sectorNodeMock.SetupGet(x => x.State).Returns(SectorNodeState.SchemeKnown);
            var sectorNode = sectorNodeMock.Object;

            return(sectorNode);
        }
Ejemplo n.º 19
0
        /// <summary>
        /// Создаёт экземпляр сектора подземелий с указанными параметрами.
        /// </summary>
        /// <param name="sectorScheme"> Схема генерации сектора. </param>
        /// <returns> Возвращает экземпляр сектора. </returns>
        public async Task <ISector> GenerateDungeonAsync(ISectorSubScheme sectorScheme)
        {
            var map = await _mapFactory.CreateAsync(sectorScheme);

            var sector = _sectorFactory.Create(map);

            var monsterRegions = map.Regions.Where(x => !x.IsStart).ToArray();

            _monsterGenerator.CreateMonsters(sector,
                                             _monsterPlayer,
                                             monsterRegions,
                                             sectorScheme);

            _chestGenerator.CreateChests(map, sectorScheme, monsterRegions);

            return(sector);
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Возвращает генератор карты.
        /// </summary>
        /// <param name="sectorScheme">Схема сектора, на основе которой будет принято решение,
        /// какой генератор карты использовать.</param>
        /// <returns> Возвращает фабрику карт для сектора. </returns>
        public IMapFactory GetMapFactory(ISectorSubScheme sectorScheme)
        {
            if (sectorScheme.MapGeneratorOptions == null)
            {
                //TODO Прописать для всех схем конкретный генератор.
                // После явного прописывания здесь нужно будет выбрасывать исключение.
                return(RoomMapFactory);
            }

            switch (sectorScheme.MapGeneratorOptions.MapGenerator)
            {
            case SchemeSectorMapGenerator.Room:
                return(RoomMapFactory);

            case SchemeSectorMapGenerator.CellularAutomaton:
                return(CellularAutomatonMapFactory);

            //TODO Прописать для всех схем конкретный генератор.
            // После явного прописывания здесь нужно будет выбрасывать исключение.
            default:
                return(RoomMapFactory);
            }
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Создаёт экземпляр сектора подземелий с указанными параметрами.
        /// </summary>
        /// <param name="sectorScheme"> Схема генерации сектора. </param>
        /// <returns> Возвращает экземпляр сектора. </returns>
        public async Task <ISector> GenerateDungeonAsync(ISectorSubScheme sectorScheme)
        {
            var mapFactory = _mapFactorySelector.GetMapFactory(sectorScheme);

            var map = await mapFactory.CreateAsync(sectorScheme);

            var sector = _sectorFactory.Create(map);

            var gameObjectRegions = map.Regions.Where(x => !x.IsStart).ToArray();

            var chestRegions = gameObjectRegions.Where(x => x.Nodes.Count() > 4);

            _chestGenerator.CreateChests(map, sectorScheme, chestRegions);

            var monsterRegions = gameObjectRegions.ToArray();

            _monsterGenerator.CreateMonsters(sector,
                                             _botPlayer,
                                             monsterRegions,
                                             sectorScheme);

            return(sector);
        }
Ejemplo n.º 22
0
        private IDropTableScheme[] GetTrashDropTables(ISectorSubScheme sectorSubScheme)
        {
            var chestDropTableSids = sectorSubScheme.ChestDropTableSids;

            if (chestDropTableSids is null)
            {
                return(Array.Empty <IDropTableScheme>());
            }

            var dropTables = new List <IDropTableScheme>();

            foreach (var chestDropSid in chestDropTableSids)
            {
                if (chestDropSid is null)
                {
                    continue;
                }

                var dropTable = _schemeService.GetScheme <IDropTableScheme>(chestDropSid);
                dropTables.Add(dropTable);
            }

            return(dropTables.ToArray());
        }
 public void BindSchemeInfo(IBiome biom, ISectorSubScheme sectorScheme)
 {
     throw new NotImplementedException();
 }
 public TestMaterializedSectorNode(ISectorSubScheme sectorScheme)
 {
     SectorScheme = sectorScheme ?? throw new ArgumentNullException(nameof(sectorScheme));
 }
Ejemplo n.º 25
0
 /// <summary>
 /// Вернуть фабрику карт.
 /// </summary>
 /// <param name="sectorScheme">Схема сектора. не используется в данном селекторе.</param>
 /// <returns> Возаращает экземпляр фабрики карт. </returns>
 public IMapFactory GetMapFactory(ISectorSubScheme sectorScheme)
 {
     return(_mapFactory);
 }
Ejemplo n.º 26
0
 public SectorNode(IBiome biom, ISectorSubScheme sectorScheme)
 {
     Biome        = biom ?? throw new ArgumentNullException(nameof(biom));
     SectorScheme = sectorScheme ?? throw new ArgumentNullException(nameof(sectorScheme));
     State        = SectorNodeState.SchemeKnown;
 }
Ejemplo n.º 27
0
        /// <summary>Создаёт монстров в секторе по указанной схеме.</summary>
        /// <param name="sector">Целевой сектор.</param>
        /// <param name="monsterPlayer">Бот, управляющий монстрами. По сути, команда монстров.</param>
        /// <param name="monsterRegions">Регионы сектора, где могут быть монстры.</param>
        /// <param name="sectorScheme">Схема сектора. Отсюда берутся параметры генерации монстров.</param>
        public void CreateMonsters(ISector sector,
                                   IBotPlayer monsterPlayer,
                                   IEnumerable <MapRegion> monsterRegions,
                                   ISectorSubScheme sectorScheme)
        {
            var rarityCounter    = new int[3];
            var rarityMaxCounter = new[] { -1, 10, 1 };

            foreach (var region in monsterRegions)
            {
                var regionNodes           = region.Nodes.OfType <HexNode>().Where(x => !x.IsObstacle);
                var containerNodes        = _propContainerManager.Items.Select(x => x.Node);
                var availableMonsterNodes = regionNodes.Except(containerNodes);

                var freeNodes = new List <IMapNode>(availableMonsterNodes);

                var monsterCount = _generatorRandomSource.RollRegionCount(
                    sectorScheme.MinRegionMonsterCount,
                    sectorScheme.RegionMonsterCount);

                for (int i = 0; i < monsterCount; i++)
                {
                    // если в комнате все места заняты
                    if (!freeNodes.Any())
                    {
                        break;
                    }

                    var currentRarity       = GetMonsterRarity(rarityCounter, rarityMaxCounter);
                    var availableSchemeSids = GetAvailableSchemeSids(sectorScheme, currentRarity);

                    var availableMonsterSchemes = availableSchemeSids.Select(x => _schemeService.GetScheme <IMonsterScheme>(x));

                    var monsterScheme = _generatorRandomSource.RollMonsterScheme(availableMonsterSchemes);

                    //TODO Восстановить патруллирование марштуров позже
                    // первый монстр ходит по маршруту
                    // остальные бродят произвольно
                    //if (i == 0)
                    //{
                    //    // генерируем маршрут обхода
                    //    var startPatrolNode = region.Nodes.First();
                    //    var endPatrolNode = region.Nodes.Last();

                    //    // генерируем моснтра
                    //    var patrolRoute = new PatrolRoute(startPatrolNode, endPatrolNode);
                    //    var monster = CreateMonster(monsterScheme, startPatrolNode, monsterPlayer);
                    //    sector.PatrolRoutes[monster] = patrolRoute;

                    //    freeNodes.Remove(monster.Node);
                    //}
                    //else
                    //{
                    var rollIndex   = _generatorRandomSource.RollNodeIndex(freeNodes.Count);
                    var monsterNode = freeNodes[rollIndex];
                    var monster     = CreateMonster(monsterScheme, monsterNode, monsterPlayer);

                    freeNodes.Remove(monster.Node);
                    //}
                }
            }
        }
Ejemplo n.º 28
0
 public SectorSchemeResult(ILocationScheme location, ISectorSubScheme sector)
 {
     LocationScheme = location ?? throw new ArgumentNullException(nameof(location));
     SectorScheme   = sector ?? throw new ArgumentNullException(nameof(sector));
 }
Ejemplo n.º 29
0
        /// <summary>
        /// Создать сундуки в секторе.
        /// </summary>
        /// <param name="map">Карта сектора. Нужна для определения доступного места для сундука.</param>
        /// <param name="sectorSubScheme">Схема сектора. По сути - настройки для размещения сундуков.</param>
        /// <param name="regions">Регионы, в которых возможно размещение сундуков.</param>
        public void CreateChests(ISectorMap map, ISectorSubScheme sectorSubScheme, IEnumerable <MapRegion> regions)
        {
            var trashDropTables    = GetTrashDropTables(sectorSubScheme);
            var treasuresDropTable = GetTreasuresDropTable();
            var chestCounter       = sectorSubScheme.TotalChestCount;

            //TODO В схемах хранить уже приведённое значение пропорции.
            var countChestRatioNormal = 1f / sectorSubScheme.RegionChestCountRatio;

            foreach (var region in regions)
            {
                var maxChestCountRaw = region.Nodes.Count() * countChestRatioNormal;
                var maxChestCount    = (int)Math.Max(maxChestCountRaw, 1);

                if (region.Nodes.Count() <= 1)
                {
                    // Для регионов, где только один узел,
                    // не создаём сундуки, иначе проход может быть загорожен.
                    // Актуально для фабрики карт на основе клеточного автомата,
                    // потому что он может генерить регионы из одного узла.

                    //TODO Попробовать проверять соседей узла.
                    // Возможно, одноклеточный регион находится в конце тупика.
                    // Тогда в нём можно разместить сундук.
                    // Критерий доступности такого региона - у узла только одни сосед.
                    continue;
                }

                var rolledCount = _chestGeneratorRandomSource.RollChestCount(maxChestCount);

                var availableNodes = from node in region.Nodes
                                     where !map.Transitions.Keys.Contains(node)
                                     where map.IsPositionAvailableForContainer(node)
                                     select node;

                var openNodes = new List <IMapNode>(availableNodes);
                for (var i = 0; i < rolledCount; i++)
                {
                    var containerPurpose = _chestGeneratorRandomSource.RollPurpose();

                    // Выбрать из коллекции доступных узлов
                    var rollIndex     = _chestGeneratorRandomSource.RollNodeIndex(openNodes.Count);
                    var containerNode = MapRegionHelper.FindNonBlockedNode(openNodes[rollIndex], map, openNodes);
                    if (containerNode == null)
                    {
                        // в этом случае будет сгенерировано на один сундук меньше.
                        // узел, с которого не удаётся найти подходящий узел, удаляем,
                        // чтобы больше его не анализировать, т.к. всё равно будет такой же исход.
                        openNodes.Remove(openNodes[rollIndex]);
                        continue;
                    }

                    // Проверка, что сундук не перегораживает проход.
                    var isValid = CheckMap(map, (HexNode)containerNode);
                    if (!isValid)
                    {
                        // в этом случае будет сгенерировано на один сундук меньше.
                        // узел, с которого не удаётся найти подходящий узел, удаляем,
                        // чтобы больше его не анализировать, т.к. всё равно будет такой же исход.
                        openNodes.Remove(openNodes[rollIndex]);
                        continue;
                    }

                    openNodes.Remove(containerNode);

                    IPropContainer container;
                    switch (containerPurpose)
                    {
                    case PropContainerPurpose.Trash:
                        container = new DropTablePropChest(containerNode, trashDropTables, _dropResolver)
                        {
                            Purpose = PropContainerPurpose.Trash
                        };
                        break;

                    case PropContainerPurpose.Treasures:
                        container = new DropTablePropChest(containerNode, treasuresDropTable, _dropResolver)
                        {
                            Purpose = PropContainerPurpose.Treasures
                        };
                        break;

                    default:
                        throw new InvalidOperationException($"Не корректное назначение {containerPurpose}.");
                    }
                    _propContainerManager.Add(container);

                    chestCounter--;

                    if (chestCounter <= 0)
                    {
                        // лимит сундуков в секторе исчерпан.
                        break;
                    }
                }
            }
        }