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();
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);
{
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))
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);
{
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))
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"}!");
}
}
}
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<TransformComponent> _xformQuery;
- private readonly DungeonConfigPrototype _gen;
+ private readonly DungeonConfig _gen;
private readonly int _seed;
private readonly Vector2i _position;
EntityLookupSystem lookup,
TileSystem tile,
SharedTransformSystem transform,
- DungeonConfigPrototype gen,
+ DungeonConfig gen,
MapGridComponent grid,
EntityUid gridUid,
int seed,
/// <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,
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();
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
return mapId;
}
- public void GenerateDungeon(DungeonConfigPrototype gen,
+ public void GenerateDungeon(DungeonConfig gen,
EntityUid gridUid,
MapGridComponent grid,
Vector2i position,
}
public async Task<List<Dungeon>> GenerateDungeonAsync(
- DungeonConfigPrototype gen,
+ DungeonConfig gen,
EntityUid gridUid,
MapGridComponent grid,
Vector2i position,
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));
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);
}
-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>
/// <summary>
/// The secret sauce, procedural generation layers that get run.
/// </summary>
- [DataField(required: true)]
+ [DataField]
public List<IDunGenLayer> Layers = new();
/// <summary>
[DataField]
public int MaxOffset;
}
+
+[Prototype]
+public sealed class DungeonConfigPrototype : DungeonConfig, IPrototype
+{
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+}
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
/// <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.
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Procedural.DungeonLayers;
+
+[Prototype]
+public sealed partial class OreDunGenPrototype : OreDunGen, IPrototype
+{
+ [IdDataField]
+ public string ID { set; get; } = default!;
+}
/// <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;
}
/// </summary>
public record struct AsteroidOffering : ISalvageMagnetOffering
{
- public DungeonConfigPrototype DungeonConfig;
+ public string Id;
+
+ public DungeonConfig DungeonConfig;
/// <summary>
/// Calculated marker layers for the asteroid.
+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;
"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);
// 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)
- {
- 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);
- layerCount++;
- layers[proto] = layerCount;
- }
- break;
- }
+ 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++)
+ {
+ var ore = weightedProto.Pick(rand);
+ config.Layers.Add(_proto.Index<OreDunGenPrototype>(ore));
+
+ var layerCount = layers.GetOrNew(ore);
+ layerCount++;
+ layers[ore] = layerCount;
}
return new AsteroidOffering
{
- DungeonConfig = configProto,
+ Id = configId,
+ DungeonConfig = config,
MarkerLayers = layers,
};
}
[OrePlasma] Plasma
[OreUranium] Uranium
[OreArtifactFragment] Artifact fragments
+ [OreBananium] Bananium
*[other] {$resource}
}
salvage-map-proto-RuinCargoBase = Ruined Cargo Storage
salvage-map-proto-SecurityChunk = Security Department Chunk
salvage-map-proto-EngineeringChunk = Engineering Department Chunk
-salvage-map-proto-OutpostArm = Overrun Outpost Arm
\ No newline at end of file
+salvage-map-proto-OutpostArm = Overrun Outpost Arm
- type: Material
- type: PhysicalComposition
materialComposition:
- RawGold: 500
+ RawGold: 100
- type: Extractable
grindableSolutionName: goldore
- type: SolutionContainerManager
- type: Material
- type: PhysicalComposition
materialComposition:
- RawDiamond: 500
+ RawDiamond: 100
- type: Extractable
grindableSolutionName: diamondore
- type: SolutionContainerManager
- type: Material
- type: PhysicalComposition
materialComposition:
- RawIron: 500
+ RawIron: 100
- type: Extractable
grindableSolutionName: ironore
- type: SolutionContainerManager
- type: Material
- type: PhysicalComposition
materialComposition:
- RawPlasma: 500
+ RawPlasma: 100
- type: PointLight
radius: 1.2
energy: 0.6
- type: Material
- type: PhysicalComposition
materialComposition:
- RawSilver: 500
+ RawSilver: 100
- type: Extractable
grindableSolutionName: silverore
- type: SolutionContainerManager
- type: Material
- type: PhysicalComposition
materialComposition:
- RawQuartz: 500
+ RawQuartz: 100
- type: Extractable
grindableSolutionName: quartzore
- type: SolutionContainerManager
- type: Material
- type: PhysicalComposition
materialComposition:
- RawUranium: 500
+ RawUranium: 100
- type: PointLight
radius: 1.2
energy: 0.8
- type: Material
- type: PhysicalComposition
materialComposition:
- RawBananium: 500
+ RawBananium: 100
- type: PointLight
radius: 1.2
energy: 1
Quantity: 0.1
- type: PhysicalComposition
materialComposition:
- Coal: 500
+ Coal: 100
- type: entity
parent: Coal
- type: Material
- type: PhysicalComposition
materialComposition:
- RawSalt: 500
+ RawSalt: 100
- type: Extractable
grindableSolutionName: saltore
- type: SolutionContainerManager
suffix: Single
components:
- type: Stack
- count: 1
\ No newline at end of file
+ count: 1
-- 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:
- !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
# Floor generation
layers:
- !type:NoiseDunGen
- tileCap: 1000
+ tileCap: 750
capStd: 32
layers:
- tile: FloorAsteroidSand
- !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
- !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
# Generate biome
- !type:BiomeDunGen
biomeTemplate: Asteroid
-
- # Generate ore veins
- - !type:BiomeMarkerLayerDunGen
- markerTemplate: AsteroidOre
--- /dev/null
+- 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
+
+
WallRockSnow: WallRockSnowTin
maxCount: 30
minGroupSize: 10
- maxGroupSize: 20
+ maxGroupSize: 15
radius: 4
- type: biomeMarkerLayer
WallRockSnow: WallRockSnowQuartz
maxCount: 30
minGroupSize: 10
- maxGroupSize: 20
+ maxGroupSize: 15
radius: 4
- type: biomeMarkerLayer
WallRockSand: WallRockSandCoal
WallRockSnow: WallRockSnowCoal
maxCount: 30
- minGroupSize: 10
- maxGroupSize: 20
+ minGroupSize: 8
+ maxGroupSize: 12
radius: 4
- type: biomeMarkerLayer
WallRockSand: WallRockSandSalt
WallRockSnow: WallRockSnowSalt
maxCount: 30
- minGroupSize: 10
- maxGroupSize: 20
+ minGroupSize: 8
+ maxGroupSize: 12
radius: 4
# Medium value
WallRockChromite: WallRockChromiteGold
WallRockSand: WallRockSandGold
WallRockSnow: WallRockSnowGold
- maxCount: 30
+ maxCount: 20
minGroupSize: 5
maxGroupSize: 10
radius: 4
WallRockChromite: WallRockChromiteSilver
WallRockSand: WallRockSandSilver
WallRockSnow: WallRockSnowSilver
- maxCount: 30
+ maxCount: 20
minGroupSize: 5
maxGroupSize: 10
radius: 4
WallRockSand: WallRockSandPlasma
WallRockSnow: WallRockSnowPlasma
maxCount: 12
- minGroupSize: 5
- maxGroupSize: 10
+ minGroupSize: 4
+ maxGroupSize: 8
radius: 4
# Uranium
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
- !type:BiomeMarkerLoot
proto: OreUranium
-- type: salvageLoot
- id: OreBananium
- guaranteed: true
- loots:
- - !type:BiomeMarkerLoot
- proto: OreBananium
-
- type: salvageLoot
id: OreDiamond
guaranteed: true
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
id: OreBananium
oreEntity: BananiumOre1
minOreYield: 1
- maxOreYield: 3
+ maxOreYield: 2
- type: ore
id: OreDiamond
- type: ore
id: OreArtifactFragment
oreEntity: ArtifactFragment1
- minOreYield: 2
- maxOreYield: 4
+ minOreYield: 1
+ maxOreYield: 3
- type: weightedRandomOre
id: RandomOreDistributionStandard