Mining Rebalance (#30920)

* first pass

* this shit too

* ok fix that shit

* buff

* actually fix that
This commit is contained in:
Nemanja
2024-08-15 21:43:54 -04:00
committed by GitHub
parent 921ed4ec5d
commit 3cdd62b0dd
22 changed files with 233 additions and 167 deletions

View File

@@ -63,7 +63,7 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
switch (offer)
{
case AsteroidOffering asteroid:
option.Title = Loc.GetString($"dungeon-config-proto-{asteroid.DungeonConfig.ID}");
option.Title = Loc.GetString($"dungeon-config-proto-{asteroid.Id}");
var layerKeys = asteroid.MarkerLayers.Keys.ToList();
layerKeys.Sort();

View File

@@ -623,14 +623,14 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
var groupSize = rand.Next(layerProto.MinGroupSize, layerProto.MaxGroupSize + 1);
// While we have remaining tiles keep iterating
while (groupSize >= 0 && remainingTiles.Count > 0)
while (groupSize > 0 && remainingTiles.Count > 0)
{
var startNode = rand.PickAndTake(remainingTiles);
frontier.Clear();
frontier.Add(startNode);
// This essentially may lead to a vein being split in multiple areas but the count matters more than position.
while (frontier.Count > 0 && groupSize >= 0)
while (frontier.Count > 0 && groupSize > 0)
{
// Need to pick a random index so we don't just get straight lines of ores.
var frontierIndex = rand.Next(frontier.Count);
@@ -643,9 +643,6 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
{
for (var y = -1; y <= 1; y++)
{
if (x != 0 && y != 0)
continue;
var neighbor = new Vector2i(node.X + x, node.Y + y);
if (frontier.Contains(neighbor) || !remainingTiles.Contains(neighbor))

View File

@@ -88,14 +88,14 @@ public sealed partial class DungeonJob
var groupSize = random.Next(gen.MinGroupSize, gen.MaxGroupSize + 1);
// While we have remaining tiles keep iterating
while (groupSize >= 0 && availableTiles.Count > 0)
while (groupSize > 0 && availableTiles.Count > 0)
{
var startNode = random.PickAndTake(availableTiles);
frontier.Clear();
frontier.Add(startNode);
// This essentially may lead to a vein being split in multiple areas but the count matters more than position.
while (frontier.Count > 0 && groupSize >= 0)
while (frontier.Count > 0 && groupSize > 0)
{
// Need to pick a random index so we don't just get straight lines of ores.
var frontierIndex = random.Next(frontier.Count);
@@ -108,9 +108,6 @@ public sealed partial class DungeonJob
{
for (var y = -1; y <= 1; y++)
{
if (x != 0 && y != 0)
continue;
var neighbor = new Vector2i(node.X + x, node.Y + y);
if (frontier.Contains(neighbor) || !availableTiles.Contains(neighbor))
@@ -142,7 +139,7 @@ public sealed partial class DungeonJob
if (groupSize > 0)
{
_sawmill.Warning($"Found remaining group size for ore veins!");
_sawmill.Warning($"Found remaining group size for ore veins of {gen.Replacement ?? "null"}!");
}
}
}

View File

@@ -44,7 +44,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<TransformComponent> _xformQuery;
private readonly DungeonConfigPrototype _gen;
private readonly DungeonConfig _gen;
private readonly int _seed;
private readonly Vector2i _position;
@@ -65,7 +65,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
EntityLookupSystem lookup,
TileSystem tile,
SharedTransformSystem transform,
DungeonConfigPrototype gen,
DungeonConfig gen,
MapGridComponent grid,
EntityUid gridUid,
int seed,
@@ -102,7 +102,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
/// <param name="reserve">Should we reserve tiles even if the config doesn't specify.</param>
private async Task<List<Dungeon>> GetDungeons(
Vector2i position,
DungeonConfigPrototype config,
DungeonConfig config,
DungeonData data,
List<IDunGenLayer> layers,
HashSet<Vector2i> reservedTiles,
@@ -139,7 +139,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
protected override async Task<List<Dungeon>?> Process()
{
_sawmill.Info($"Generating dungeon {_gen.ID} with seed {_seed} on {_entManager.ToPrettyString(_gridUid)}");
_sawmill.Info($"Generating dungeon {_gen} with seed {_seed} on {_entManager.ToPrettyString(_gridUid)}");
_grid.CanSplit = false;
var random = new Random(_seed);
var position = (_position + random.NextPolarVector2(_gen.MinOffset, _gen.MaxOffset)).Floored();
@@ -177,7 +177,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
int seed,
Random random)
{
_sawmill.Debug($"Doing postgen {layer.GetType()} for {_gen.ID} with seed {_seed}");
_sawmill.Debug($"Doing postgen {layer.GetType()} for {_gen} with seed {_seed}");
// If there's a way to just call the methods directly for the love of god tell me.
// Some of these don't care about reservedtiles because they only operate on dungeon tiles (which should

View File

@@ -183,7 +183,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
return mapId;
}
public void GenerateDungeon(DungeonConfigPrototype gen,
public void GenerateDungeon(DungeonConfig gen,
EntityUid gridUid,
MapGridComponent grid,
Vector2i position,
@@ -214,7 +214,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
}
public async Task<List<Dungeon>> GenerateDungeonAsync(
DungeonConfigPrototype gen,
DungeonConfig gen,
EntityUid gridUid,
MapGridComponent grid,
Vector2i position,

View File

@@ -175,7 +175,7 @@ public sealed class SpawnSalvageMissionJob : Job<bool>
var dungeonOffset = new Vector2(0f, dungeonOffsetDistance);
dungeonOffset = dungeonRotation.RotateVec(dungeonOffset);
var dungeonMod = _prototypeManager.Index<SalvageDungeonModPrototype>(mission.Dungeon);
var dungeonConfig = _prototypeManager.Index<DungeonConfigPrototype>(dungeonMod.Proto);
var dungeonConfig = _prototypeManager.Index(dungeonMod.Proto);
var dungeons = await WaitAsyncTask(_dungeon.GenerateDungeonAsync(dungeonConfig, mapUid, grid, (Vector2i) dungeonOffset,
_missionParams.Seed));

View File

@@ -17,7 +17,12 @@ public partial struct MinMax
Max = max;
}
public int Next(IRobustRandom random)
public readonly int Next(IRobustRandom random)
{
return random.Next(Min, Max + 1);
}
public readonly int Next(System.Random random)
{
return random.Next(Min, Max + 1);
}

View File

@@ -1,14 +1,10 @@
using Content.Shared.Procedural.PostGeneration;
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural;
[Prototype]
public sealed partial class DungeonConfigPrototype : IPrototype
[Virtual, DataDefinition]
public partial class DungeonConfig
{
[IdDataField]
public string ID { get; private set; } = default!;
/// <summary>
/// <see cref="Data"/>
/// </summary>
@@ -18,7 +14,7 @@ public sealed partial class DungeonConfigPrototype : IPrototype
/// <summary>
/// The secret sauce, procedural generation layers that get run.
/// </summary>
[DataField(required: true)]
[DataField]
public List<IDunGenLayer> Layers = new();
/// <summary>
@@ -51,3 +47,10 @@ public sealed partial class DungeonConfigPrototype : IPrototype
[DataField]
public int MaxOffset;
}
[Prototype]
public sealed class DungeonConfigPrototype : DungeonConfig, IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
}

View File

@@ -3,7 +3,7 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonGenerators;
/// <summary>
/// Runs another <see cref="DungeonConfigPrototype"/>.
/// Runs another <see cref="DungeonConfig"/>.
/// Used for storing data on 1 system.
/// </summary>
public sealed partial class PrototypeDunGen : IDunGenLayer

View File

@@ -8,7 +8,8 @@ namespace Content.Shared.Procedural.DungeonLayers;
/// <remarks>
/// Generates on top of existing entities for sanity reasons moreso than performance.
/// </remarks>
public sealed partial class OreDunGen : IDunGenLayer
[Virtual]
public partial class OreDunGen : IDunGenLayer
{
/// <summary>
/// If the vein generation should occur on top of existing entities what are we replacing.

View File

@@ -0,0 +1,10 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Procedural.DungeonLayers;
[Prototype]
public sealed partial class OreDunGenPrototype : OreDunGen, IPrototype
{
[IdDataField]
public string ID { set; get; } = default!;
}

View File

@@ -23,6 +23,6 @@ public sealed partial class SalvageDungeonModPrototype : IPrototype, IBiomeSpeci
/// <summary>
/// The config to use for spawning the dungeon.
/// </summary>
[DataField("proto", customTypeSerializer: typeof(PrototypeIdSerializer<DungeonConfigPrototype>), required: true)]
public string Proto = string.Empty;
[DataField(required: true)]
public ProtoId<DungeonConfigPrototype> Proto = string.Empty;
}

View File

@@ -7,7 +7,9 @@ namespace Content.Shared.Salvage.Magnet;
/// </summary>
public record struct AsteroidOffering : ISalvageMagnetOffering
{
public DungeonConfigPrototype DungeonConfig;
public string Id;
public DungeonConfig DungeonConfig;
/// <summary>
/// Calculated marker layers for the asteroid.

View File

@@ -1,8 +1,9 @@
using Content.Shared.Destructible.Thresholds;
using Content.Shared.Procedural;
using Content.Shared.Procedural.PostGeneration;
using Content.Shared.Procedural.DungeonLayers;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Content.Shared.Salvage.Magnet;
using Content.Shared.Store;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -20,6 +21,10 @@ public abstract partial class SharedSalvageSystem
"SwissCheeseAsteroid"
};
private readonly ProtoId<WeightedRandomPrototype> _asteroidOreWeights = "AsteroidOre";
private readonly MinMax _asteroidOreCount = new(5, 7);
public ISalvageMagnetOffering GetSalvageOffering(int seed)
{
var rand = new System.Random(seed);
@@ -27,33 +32,40 @@ public abstract partial class SharedSalvageSystem
// Asteroid seed
if (seed % 2 == 0)
{
var config = _asteroidConfigs[rand.Next(_asteroidConfigs.Count)];
var configProto = _proto.Index(config);
var configId = _asteroidConfigs[rand.Next(_asteroidConfigs.Count)];
var configProto =_proto.Index(configId);
var layers = new Dictionary<string, int>();
// If we ever add more random layers will need to Next on these.
foreach (var layer in configProto.Layers)
var data = new DungeonData();
data.Apply(configProto.Data);
var config = new DungeonConfig()
{
switch (layer)
Data = data,
Layers = new(configProto.Layers),
MaxCount = configProto.MaxCount,
MaxOffset = configProto.MaxOffset,
MinCount = configProto.MinCount,
MinOffset = configProto.MinOffset,
ReserveTiles = configProto.ReserveTiles
};
var count = _asteroidOreCount.Next(rand);
var weightedProto = _proto.Index(_asteroidOreWeights);
for (var i = 0; i < count; i++)
{
case BiomeDunGen:
rand.Next();
break;
case BiomeMarkerLayerDunGen marker:
for (var i = 0; i < marker.Count; i++)
{
var proto = _proto.Index(marker.MarkerTemplate).Pick(rand);
var layerCount = layers.GetOrNew(proto);
var ore = weightedProto.Pick(rand);
config.Layers.Add(_proto.Index<OreDunGenPrototype>(ore));
var layerCount = layers.GetOrNew(ore);
layerCount++;
layers[proto] = layerCount;
}
break;
}
layers[ore] = layerCount;
}
return new AsteroidOffering
{
DungeonConfig = configProto,
Id = configId,
DungeonConfig = config,
MarkerLayers = layers,
};
}

View File

@@ -18,6 +18,7 @@ salvage-magnet-resources = {$resource ->
[OrePlasma] Plasma
[OreUranium] Uranium
[OreArtifactFragment] Artifact fragments
[OreBananium] Bananium
*[other] {$resource}
}

View File

@@ -48,7 +48,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawGold: 500
RawGold: 100
- type: Extractable
grindableSolutionName: goldore
- type: SolutionContainerManager
@@ -79,7 +79,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawDiamond: 500
RawDiamond: 100
- type: Extractable
grindableSolutionName: diamondore
- type: SolutionContainerManager
@@ -110,7 +110,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawIron: 500
RawIron: 100
- type: Extractable
grindableSolutionName: ironore
- type: SolutionContainerManager
@@ -141,7 +141,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawPlasma: 500
RawPlasma: 100
- type: PointLight
radius: 1.2
energy: 0.6
@@ -177,7 +177,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawSilver: 500
RawSilver: 100
- type: Extractable
grindableSolutionName: silverore
- type: SolutionContainerManager
@@ -208,7 +208,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawQuartz: 500
RawQuartz: 100
- type: Extractable
grindableSolutionName: quartzore
- type: SolutionContainerManager
@@ -239,7 +239,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawUranium: 500
RawUranium: 100
- type: PointLight
radius: 1.2
energy: 0.8
@@ -278,7 +278,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawBananium: 500
RawBananium: 100
- type: PointLight
radius: 1.2
energy: 1
@@ -334,7 +334,7 @@
Quantity: 0.1
- type: PhysicalComposition
materialComposition:
Coal: 500
Coal: 100
- type: entity
parent: Coal
@@ -357,7 +357,7 @@
- type: Material
- type: PhysicalComposition
materialComposition:
RawSalt: 500
RawSalt: 100
- type: Extractable
grindableSolutionName: saltore
- type: SolutionContainerManager

View File

@@ -1,24 +1,10 @@
- type: weightedRandom
id: AsteroidOre
weights:
OreIron: 1.0
OreQuartz: 1.0
OreCoal: 1.0
OreSalt: 1.0
OreGold: 0.25
OreDiamond: 0.05
OreSilver: 0.25
OrePlasma: 0.15
OreUranium: 0.15
OreArtifactFragment: 0.15
# Large asteroids, typically 1
- type: dungeonConfig
id: BlobAsteroid
# Floor generation
layers:
- !type:NoiseDunGen
tileCap: 1500
tileCap: 1000
capStd: 32
iterations: 3
layers:
@@ -35,10 +21,6 @@
- !type:BiomeDunGen
biomeTemplate: Asteroid
# Generate ore veins
- !type:BiomeMarkerLayerDunGen
markerTemplate: AsteroidOre
# Multiple smaller asteroids
# This is a pain so we generate fewer tiles
- type: dungeonConfig
@@ -46,7 +28,7 @@
# Floor generation
layers:
- !type:NoiseDunGen
tileCap: 1000
tileCap: 750
capStd: 32
layers:
- tile: FloorAsteroidSand
@@ -62,17 +44,13 @@
- !type:BiomeDunGen
biomeTemplate: Asteroid
# Generate ore veins
- !type:BiomeMarkerLayerDunGen
markerTemplate: AsteroidOre
# Long and spindly, less smooth than blob
- type: dungeonConfig
id: SpindlyAsteroid
# Floor generation
layers:
- !type:NoiseDunGen
tileCap: 1500
tileCap: 1000
capStd: 32
layers:
- tile: FloorAsteroidSand
@@ -89,17 +67,13 @@
- !type:BiomeDunGen
biomeTemplate: Asteroid
# Generate ore veins
- !type:BiomeMarkerLayerDunGen
markerTemplate: AsteroidOre
# Lots of holes in it
- type: dungeonConfig
id: SwissCheeseAsteroid
# Floor generation
layers:
- !type:NoiseDunGen
tileCap: 1500
tileCap: 1000
capStd: 32
layers:
- tile: FloorAsteroidSand
@@ -114,7 +88,3 @@
# Generate biome
- !type:BiomeDunGen
biomeTemplate: Asteroid
# Generate ore veins
- !type:BiomeMarkerLayerDunGen
markerTemplate: AsteroidOre

View File

@@ -0,0 +1,95 @@
- type: weightedRandom
id: AsteroidOre
weights:
OreIron: 1.0
OreQuartz: 1.0
OreCoal: 0.33
OreSalt: 0.25
OreGold: 0.25
OreSilver: 0.25
OrePlasma: 0.15
OreUranium: 0.15
OreArtifactFragment: 0.10
OreBananium: 0.10
- type: oreDunGen
id: OreIron
replacement: AsteroidRock
entity: AsteroidRockTin
count: 5
minGroupSize: 5
maxGroupSize: 7
- type: oreDunGen
id: OreQuartz
replacement: AsteroidRock
entity: AsteroidRockQuartz
count: 5
minGroupSize: 5
maxGroupSize: 7
- type: oreDunGen
id: OreCoal
replacement: AsteroidRock
entity: AsteroidRockCoal
count: 3
minGroupSize: 5
maxGroupSize: 7
- type: oreDunGen
id: OreSalt
replacement: AsteroidRock
entity: AsteroidRockSalt
count: 3
minGroupSize: 5
maxGroupSize: 7
- type: oreDunGen
id: OreGold
replacement: AsteroidRock
entity: AsteroidRockGold
count: 4
minGroupSize: 4
maxGroupSize: 6
- type: oreDunGen
id: OreSilver
replacement: AsteroidRock
entity: AsteroidRockSilver
count: 4
minGroupSize: 4
maxGroupSize: 6
- type: oreDunGen
id: OrePlasma
replacement: AsteroidRock
entity: AsteroidRockPlasma
count: 4
minGroupSize: 3
maxGroupSize: 6
- type: oreDunGen
id: OreUranium
replacement: AsteroidRock
entity: AsteroidRockUranium
count: 4
minGroupSize: 3
maxGroupSize: 6
- type: oreDunGen
id: OreBananium
replacement: AsteroidRock
entity: AsteroidRockBananium
count: 6
minGroupSize: 3
maxGroupSize: 6
- type: oreDunGen
id: OreArtifactFragment
replacement: AsteroidRock
entity: AsteroidRockArtifactFragment
count: 5
minGroupSize: 1
maxGroupSize: 2

View File

@@ -10,7 +10,7 @@
WallRockSnow: WallRockSnowTin
maxCount: 30
minGroupSize: 10
maxGroupSize: 20
maxGroupSize: 15
radius: 4
- type: biomeMarkerLayer
@@ -23,7 +23,7 @@
WallRockSnow: WallRockSnowQuartz
maxCount: 30
minGroupSize: 10
maxGroupSize: 20
maxGroupSize: 15
radius: 4
- type: biomeMarkerLayer
@@ -36,8 +36,8 @@
WallRockSand: WallRockSandCoal
WallRockSnow: WallRockSnowCoal
maxCount: 30
minGroupSize: 10
maxGroupSize: 20
minGroupSize: 8
maxGroupSize: 12
radius: 4
- type: biomeMarkerLayer
@@ -50,8 +50,8 @@
WallRockSand: WallRockSandSalt
WallRockSnow: WallRockSnowSalt
maxCount: 30
minGroupSize: 10
maxGroupSize: 20
minGroupSize: 8
maxGroupSize: 12
radius: 4
# Medium value
@@ -65,7 +65,7 @@
WallRockChromite: WallRockChromiteGold
WallRockSand: WallRockSandGold
WallRockSnow: WallRockSnowGold
maxCount: 30
maxCount: 20
minGroupSize: 5
maxGroupSize: 10
radius: 4
@@ -80,7 +80,7 @@
WallRockChromite: WallRockChromiteSilver
WallRockSand: WallRockSandSilver
WallRockSnow: WallRockSnowSilver
maxCount: 30
maxCount: 20
minGroupSize: 5
maxGroupSize: 10
radius: 4
@@ -97,8 +97,8 @@
WallRockSand: WallRockSandPlasma
WallRockSnow: WallRockSnowPlasma
maxCount: 12
minGroupSize: 5
maxGroupSize: 10
minGroupSize: 4
maxGroupSize: 8
radius: 4
# Uranium
@@ -111,23 +111,9 @@
WallRockChromite: WallRockChromiteUranium
WallRockSand: WallRockSandUranium
WallRockSnow: WallRockSnowUranium
maxCount: 12
minGroupSize: 5
maxGroupSize: 10
radius: 4
- type: biomeMarkerLayer
id: OreBananium
entityMask:
AsteroidRock: AsteroidRockBananium
WallRock: WallRockBananium
WallRockBasalt: WallRockBasaltBananium
WallRockChromite: WallRockChromiteBananium
WallRockSand: WallRockSandBananium
WallRockSnow: WallRockSnowBananium
maxCount: 12
minGroupSize: 5
maxGroupSize: 10
maxCount: 15
minGroupSize: 4
maxGroupSize: 8
radius: 4
- type: biomeMarkerLayer

View File

@@ -171,13 +171,6 @@
- !type:BiomeMarkerLoot
proto: OreUranium
- type: salvageLoot
id: OreBananium
guaranteed: true
loots:
- !type:BiomeMarkerLoot
proto: OreBananium
- type: salvageLoot
id: OreDiamond
guaranteed: true

View File

@@ -26,62 +26,56 @@
replacement: IronRock
entity: IronRockIron
count: 50
minGroupSize: 20
maxGroupSize: 30
minGroupSize: 10
maxGroupSize: 15
- !type:OreDunGen
replacement: IronRock
entity: IronRockCoal
count: 50
minGroupSize: 20
maxGroupSize: 30
minGroupSize: 8
maxGroupSize: 12
- !type:OreDunGen
replacement: IronRock
entity: IronRockQuartz
count: 50
minGroupSize: 20
maxGroupSize: 30
minGroupSize: 10
maxGroupSize: 15
- !type:OreDunGen
replacement: IronRock
entity: IronRockSalt
count: 50
minGroupSize: 20
maxGroupSize: 30
minGroupSize: 8
maxGroupSize: 12
- !type:OreDunGen
replacement: IronRock
entity: IronRockGold
count: 50
minGroupSize: 10
maxGroupSize: 20
count: 40
minGroupSize: 8
maxGroupSize: 12
- !type:OreDunGen
replacement: IronRock
entity: IronRockSilver
count: 50
minGroupSize: 10
maxGroupSize: 20
count: 40
minGroupSize: 8
maxGroupSize: 12
- !type:OreDunGen
replacement: IronRock
entity: IronRockPlasma
count: 50
minGroupSize: 10
maxGroupSize: 20
count: 35
minGroupSize: 4
maxGroupSize: 8
- !type:OreDunGen
replacement: IronRock
entity: IronRockUranium
count: 50
minGroupSize: 10
maxGroupSize: 20
- !type:OreDunGen
replacement: IronRock
entity: IronRockBananium
count: 50
minGroupSize: 10
maxGroupSize: 20
count: 35
minGroupSize: 4
maxGroupSize: 8
- !type:OreDunGen
replacement: IronRock
entity: IronRockArtifactFragment
count: 50
minGroupSize: 2
maxGroupSize: 4
count: 25
minGroupSize: 1
maxGroupSize: 3
- !type:OreDunGen
replacement: IronRock
entity: IronRockDiamond

View File

@@ -56,7 +56,7 @@
id: OreBananium
oreEntity: BananiumOre1
minOreYield: 1
maxOreYield: 3
maxOreYield: 2
- type: ore
id: OreDiamond
@@ -83,8 +83,8 @@
- type: ore
id: OreArtifactFragment
oreEntity: ArtifactFragment1
minOreYield: 2
maxOreYield: 4
minOreYield: 1
maxOreYield: 3
- type: weightedRandomOre
id: RandomOreDistributionStandard