/// <summary> /// Случайный выбор координат для размещения элемента интерьера. /// </summary> /// <param name="regionDraftCoords"> Координаты региона, среди которых можно выбирать позиции элементов интерьера. </param> /// <returns> Возвращает набор метаданных об элементах интерьера. </returns> public InteriorObjectMeta[] RollInteriorObjects(OffsetCoords[] regionDraftCoords) { if (regionDraftCoords is null) { throw new ArgumentNullException(nameof(regionDraftCoords)); } // Получаем все координаты, которые не прижаты к краю. // Ставить препятсвия на краю нельзя, // потому что коридор может начаться с препятсвия. // Коридоры просто строятся от ближайшей точки региона. var coordsInCenter = GetAvailableCoords(regionDraftCoords).ToArray(); // Выбираем все координаты, по которым может пройти персонаж размером 7. var passableCoords = GetAllPassableSize7Coords(regionDraftCoords); var openCoords = new List <OffsetCoords>(coordsInCenter); if (!openCoords.Any()) { return(Array.Empty <InteriorObjectMeta>()); } var count = openCoords.Count / 4; var resultMetaList = new List <InteriorObjectMeta>(); for (var i = 0; i < count; i++) { // Выполняем 3 попытки на размещение элемента декора. // 2 из них могут быть неудачными (например, элемент // декора перекрывает проход к какой-либо доступной ячейке). for (var retryIndex = 0; retryIndex < RETRY_COUNT; retryIndex++) { var isValid = TryRollInteriorCoord(openCoords.ToArray(), passableCoords, out var rolledCoord); // Вне зависимости от корректности rolledCoord // убираем его из открытых координат. // В случае если он перекрывал одну из доступных ячеек. // Позже такая координата может стать корректной (например, // когда будет полукольцо из препятсвий). // Но мы пренебрегаем этим в целях производительности. openCoords.Remove(rolledCoord); if (isValid) { var interiorMeta = new InteriorObjectMeta(rolledCoord); resultMetaList.Add(interiorMeta); break; } if (!openCoords.Any()) { break; } } if (!openCoords.Any()) { break; } } return(resultMetaList.ToArray()); }