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)); }
/// <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); } }
/// <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); }
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); } }
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)); }
/// <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); }
private static IEnumerable <RoomTransition> CreateTransitions(ISectorSubScheme sectorScheme) { if (sectorScheme.TransSectorSids == null) { return(new[] { RoomTransition.CreateGlobalExit() }); } return(sectorScheme.TransSectorSids.Select(sid => new RoomTransition(sid))); }
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() });
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); }
/// <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); } } } }
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; }
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()); }
/// <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); }
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); }
/// <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; } } } }
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); }
/// <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); }
/// <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); } }
/// <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); }
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)); }
/// <summary> /// Вернуть фабрику карт. /// </summary> /// <param name="sectorScheme">Схема сектора. не используется в данном селекторе.</param> /// <returns> Возаращает экземпляр фабрики карт. </returns> public IMapFactory GetMapFactory(ISectorSubScheme sectorScheme) { return(_mapFactory); }
public SectorNode(IBiome biom, ISectorSubScheme sectorScheme) { Biome = biom ?? throw new ArgumentNullException(nameof(biom)); SectorScheme = sectorScheme ?? throw new ArgumentNullException(nameof(sectorScheme)); State = SectorNodeState.SchemeKnown; }
/// <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); //} } } }
public SectorSchemeResult(ILocationScheme location, ISectorSubScheme sector) { LocationScheme = location ?? throw new ArgumentNullException(nameof(location)); SectorScheme = sector ?? throw new ArgumentNullException(nameof(sector)); }
/// <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; } } } }