using Cysharp.Threading.Tasks; using Unity.Mathematics; using UnityEngine; using Random = Unity.Mathematics.Random; namespace DanieleMarotta.RiversongCodeShowcase { public class TreeResourcesGeneratorOperation : IWorldGeneratorOperation { [InjectService] private GameConfig _config; [InjectService] private ITileSpace _tileSpace; public async UniTask Execute(World world) { await GenerateTreesAsync(world); } private async UniTask GenerateTreesAsync(World world) { var config = _config.WorldGen; var treeDefinition = await config.Trees.TreeDefinition.LoadAssetAsync(); var horizontalResolution = Mathf.FloorToInt(world.Size.x / config.Trees.Spacing); var verticalResolution = Mathf.FloorToInt(world.Size.y / config.Trees.Spacing); var random = new Random((uint)world.Seed); var noiseSalt = world.Seed % ushort.MaxValue * 37; for (var x = 0; x < horizontalResolution; x++) for (var z = 0; z < verticalResolution; z++) { var treePosition = new Vector3((x + 0.5f) * config.Trees.Spacing, 0, (z + 0.5f) * config.Trees.Spacing); if (z % 2 == 0) treePosition.x += 0.5f * config.Trees.Spacing; treePosition.x += Mathf.Lerp(config.Trees.OffsetRange.x, config.Trees.OffsetRange.y, random.NextFloat()); treePosition.z += Mathf.Lerp(config.Trees.OffsetRange.x, config.Trees.OffsetRange.y, random.NextFloat()); var tile = _tileSpace.WorldToTile(treePosition); if (!EnoughSpaceAround(world, tile)) continue; var h = world.Heightmap.GetValue(tile); if (h == 0) continue; treePosition.y = h; var noiseSamplePos = new float2(h * 997 + x, noiseSalt + z) * config.Trees.NoiseScale; var n = noise.snoise(noiseSamplePos); if (n < 1 - config.Trees.Coverage && random.NextFloat() < 1 - config.Trees.RandomTreeChance) continue; world.RawResources.AddResourceNode( new ResourceNode { Id = 1 + world.RawResources.Count, DefinitionId = treeDefinition.RuntimeId, Position = treePosition, Tile = tile, Elevation = _tileSpace.GetElevation(treePosition.y), CanBeDeleted = true }); world.BlockMap.AddReason(tile, BlockReason.RawResource); } } private static bool EnoughSpaceAround(World world, int2 center) { const int radius = 1; for (var x = -radius; x <= radius; x++) for (var y = -radius; y <= radius; y++) { var p = center + new int2(x, y); if (!world.Contains(p)) continue; if (world.Fertility.GetValue(p).MaxFertility > 0) return false; } return true; } } }