using System.Collections.Generic; using Cysharp.Threading.Tasks; using Unity.Mathematics; using UnityEngine; using Random = Unity.Mathematics.Random; namespace DanieleMarotta.RiversongCodeShowcase { public class MapDataInitializationOperation : IWorldGeneratorOperation { private const int HeightStep = 16; [InjectService] private GameConfig _config; public async UniTask Execute(World world) { var mapTexture = await GetRandomMapTextureAsync(world.Seed); var worldSize = new int2(mapTexture.width, mapTexture.height); world.Size = worldSize; world.Heightmap = new WorldHeightmap(worldSize); world.BlockMap = new BlockMap(worldSize); world.Fertility = new FertilityMap(worldSize); world.WaterMap = new WaterMap(worldSize); world.EntityIdMap = new EntityIdMap(worldSize); world.RoadNetwork = new RoadNetwork(worldSize); ApplyMapTexture(world, mapTexture); await InitializeWaterMapAsync(world); } private async UniTask GetRandomMapTextureAsync(int seed) { var config = _config.WorldGen; var mapIndex = new Random((uint)seed).NextInt(config.MapTextures.Count); var mapTexture = await config.MapTextures[mapIndex].LoadAssetAsync(); return mapTexture; } private void ApplyMapTexture(World world, Texture2D mapTexture) { var mapData = mapTexture.GetPixelData(0); var blockMapData = world.BlockMap.GetNativeArray(); var fertilityData = world.Fertility.GetNativeArray(); for (var tileIndex = 0; tileIndex < mapData.Length; tileIndex++) { var color = mapData[tileIndex]; var h = color.r / HeightStep; world.Heightmap.SetValue(tileIndex, h); if (h != _config.GeneralSettings.BaseElevation) { blockMapData[tileIndex] |= BlockReason.InvalidElevation; continue; } var fertility = (float)color.g / byte.MaxValue; fertilityData[tileIndex] = new FertilityMapValue { CurrentFertility = fertility, MaxFertility = fertility, LockId = Entity.InvalidId }; if (color.b > 0) { var stride = world.Size.x; var tile = new int2(tileIndex % stride, tileIndex / stride); world.CritterHerdsState.EligibleCenters.Add(tile); } } } private async UniTask InitializeWaterMapAsync(World world) { const int MaxWaterDistance = 3; var budget = new AsyncBudget(world.Size.x * 8); var open = new Queue(); foreach (var tile in TileRange.From(0, world.Size - 1)) { if (BelowWater(world, tile)) open.Enqueue(tile); else world.WaterMap.SetValue(tile, int.MaxValue); await budget.TickAsync(); } while (open.Count > 0) { var current = open.Dequeue(); var currentDistance = world.WaterMap.GetValue(current); if (currentDistance >= MaxWaterDistance) continue; foreach (var direction in DirectionVectors.Directions8) { var other = current + direction; if (!world.Contains(other)) continue; var distance = BelowWater(world, other) ? 0 : currentDistance + 1; if (distance >= world.WaterMap.GetValue(other)) continue; world.WaterMap.SetValue(other, distance); if (distance < MaxWaterDistance) open.Enqueue(other); } await budget.TickAsync(); } } private bool BelowWater(World world, int2 tile) { return world.Heightmap.GetValue(tile) < _config.GeneralSettings.BaseElevation; } } }