Files
riversong-code-showcase/Source/Riversong/Game/WorldGen/Operations/StoneResourcesGeneratorOperation.cs
2026-05-21 16:04:49 +02:00

127 lines
4.6 KiB
C#

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<ResourceNodeDefinition>();
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<int2>(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<int2> alreadyPlaced, int spacing)
{
foreach (var other in alreadyPlaced)
if (TileMath.StepCount(tile, other) < spacing)
return true;
return false;
}
}
}