using System.Collections.Generic; using Cysharp.Threading.Tasks; using Unity.Mathematics; using Random = Unity.Mathematics.Random; namespace DanieleMarotta.RiversongCodeShowcase { public class StoneResourcesGeneratorOperation : IWorldGeneratorOperation { [InjectService] private GameConfig _config; [InjectService] private ITileSpace _tileSpace; public async UniTask Execute(World world) { return; // Temporarily disabled var blockMap = world.BlockMap; var resourcesState = world.RawResources; var config = _config.WorldGen.Stone; var stoneDefinition = await config.StoneDefinition.LoadAssetAsync(); var random = new Random((uint)world.Seed); var count = random.NextInt(config.Count.x, config.Count.y + 1); var candidates = new List<(int2, int2, Directions)>(); foreach (var tile in TileRange.From(1, world.Size - 2)) if (ValidateCandidate(world, tile, out var side, out var orientation)) candidates.Add((tile, side, orientation)); var current = new List(count); while (candidates.Count > 0 && current.Count < count) { var i = random.NextInt(candidates.Count); var (tile, side, orientation) = candidates[i]; candidates[i] = candidates[^1]; candidates.RemoveAt(candidates.Count - 1); if (TooCloseToOtherResources(tile, current, config.Spacing)) continue; current.Add(tile); var position = _tileSpace.TileToWorld(tile); resourcesState.AddResourceNode( new ResourceNode { Id = 1 + resourcesState.Count, DefinitionId = stoneDefinition.RuntimeId, Position = position, Tile = tile, Orientation = orientation, Elevation = _config.GeneralSettings.BaseElevation }); blockMap.AddReason(tile, BlockReason.RawResource); blockMap.AddReason(tile + side, BlockReason.RawResource); blockMap.AddReason(tile - side, BlockReason.RawResource); } } private bool ValidateCandidate(World world, int2 tile, out int2 side, out Directions orientation) { side = default; orientation = default; return world.Heightmap.GetValue(tile) == _config.GeneralSettings.BaseElevation && !world.BlockMap.IsBlocked(tile) && NextToCliff(world, tile, out side, out orientation); } private bool NextToCliff(World world, int2 tile, out int2 side, out Directions orientation) { side = new int2(0, 1); if (NextToCliff(world, tile, new int2(1, 0), side)) { orientation = Directions.East; return true; } if (NextToCliff(world, tile, new int2(-1, 0), side)) { orientation = Directions.West; return true; } side = new int2(1, 0); if (NextToCliff(world, tile, new int2(0, 1), side)) { orientation = Directions.North; return true; } if (NextToCliff(world, tile, new int2(0, -1), side)) { orientation = Directions.South; return true; } orientation = default; return false; } private bool NextToCliff(World world, int2 bottomTile, int2 direction, int2 side) { var topTile = bottomTile + direction; var baseElevation = _config.GeneralSettings.BaseElevation; return world.Heightmap.GetValue(bottomTile + side) == baseElevation && world.Heightmap.GetValue(bottomTile - side) == baseElevation && world.Heightmap.GetValue(topTile) > baseElevation && world.Heightmap.GetValue(topTile + side) > baseElevation && world.Heightmap.GetValue(topTile - side) > baseElevation; } private static bool TooCloseToOtherResources(int2 tile, List alreadyPlaced, int spacing) { foreach (var other in alreadyPlaced) if (TileMath.StepCount(tile, other) < spacing) return true; return false; } } }