Salvage expeditions (#12745)

This commit is contained in:
metalgearsloth
2023-04-20 10:43:13 +10:00
committed by GitHub
parent 486d7c179e
commit 122350f19c
79 changed files with 2764 additions and 662 deletions

View File

@@ -1,21 +0,0 @@
namespace Content.Shared.Salvage.Expeditions.Extraction;
public sealed class SalvageExtraction : ISalvageMission
{
/// <summary>
/// Minimum weight to be used for a wave.
/// </summary>
[DataField("minWaveWeight")] public float MinWaveWeight = 5;
/// <summary>
/// Minimum time between 2 waves. Roughly the end of one to the start of another.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("waveCooldown")]
public TimeSpan WaveCooldown = TimeSpan.FromSeconds(60);
/// <summary>
/// How much weight accumulates per second while the expedition is active.
/// </summary>
[DataField("weightAccumulator")]
public float WeightAccumulator = 0.1f;
}

View File

@@ -1,7 +0,0 @@
namespace Content.Shared.Salvage.Expeditions;
public interface IFactionExpeditionConfig
{
}

View File

@@ -0,0 +1,11 @@
namespace Content.Shared.Salvage.Expeditions.Modifiers;
public interface ISalvageMod
{
/// <summary>
/// Player-friendly version describing this modifier.
/// </summary>
string Description { get; }
float Cost { get; }
}

View File

@@ -0,0 +1,31 @@
using Content.Shared.Parallax.Biomes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Salvage.Expeditions.Modifiers;
/// <summary>
/// Affects the biome to be used for salvage.
/// </summary>
[Prototype("salvageBiomeMod")]
public sealed class SalvageBiomeMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
/// <summary>
/// Is weather allowed to apply to this biome.
/// </summary>
[DataField("weather")]
public bool Weather = true;
[DataField("biome", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<BiomeTemplatePrototype>))]
public string? BiomePrototype;
}

View File

@@ -0,0 +1,29 @@
using Content.Shared.Procedural;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Salvage.Expeditions.Modifiers;
[Prototype("salvageDungeonMod")]
public sealed class SalvageDungeonMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
[DataField("proto", customTypeSerializer:typeof(PrototypeIdSerializer<DungeonConfigPrototype>))]
public string Proto = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
/// <summary>
/// Biomes this dungeon can occur in.
/// </summary>
[DataField("biomeMods", customTypeSerializer:typeof(PrototypeIdListSerializer<SalvageBiomeMod>))]
public List<string>? BiomeMods;
}

View File

@@ -0,0 +1,26 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Salvage.Expeditions.Modifiers;
[Prototype("salvageLightMod")]
public sealed class SalvageLightMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
[DataField("color", required: true)] public Color? Color;
/// <summary>
/// Biomes that this color applies to.
/// </summary>
[DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer<SalvageBiomeMod>))]
public List<string>? Biomes;
}

View File

@@ -0,0 +1,20 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Salvage.Expeditions.Modifiers;
/// <summary>
/// Generic modifiers with no additional data
/// </summary>
[Prototype("salvageMod")]
public sealed class SalvageMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
}

View File

@@ -0,0 +1,23 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Salvage.Expeditions.Modifiers;
[Prototype("salvageTimeMod")]
public sealed class SalvageTimeMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
[DataField("minDuration")]
public int MinDuration = 600;
[DataField("maxDuration")]
public int MaxDuration = 660;
}

View File

@@ -0,0 +1,30 @@
using Content.Shared.Parallax.Biomes;
using Content.Shared.Weather;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Salvage.Expeditions.Modifiers;
[Prototype("salvageWeatherMod")]
public sealed class SalvageWeatherMod : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
[DataField("weather", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<WeatherPrototype>))]
public string WeatherPrototype = string.Empty;
/// <summary>
/// Whitelist for biomes. If empty assumed any allowed.
/// </summary>
[DataField("biomes", customTypeSerializer:typeof(PrototypeIdListSerializer<BiomeTemplatePrototype>))]
public List<string> Biomes = new();
}

View File

@@ -1,19 +1,28 @@
using Content.Shared.Salvage.Expeditions.Modifiers;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
namespace Content.Shared.Salvage.Expeditions;
[Prototype("salvageFaction")]
public sealed class SalvageFactionPrototype : IPrototype
public sealed class SalvageFactionPrototype : IPrototype, ISalvageMod
{
[IdDataField] public string ID { get; } = default!;
[DataField("desc")] public string Description { get; } = string.Empty;
/// <summary>
/// Cost for difficulty modifiers.
/// </summary>
[DataField("cost")]
public float Cost { get; } = 0f;
[ViewVariables(VVAccess.ReadWrite), DataField("groups", required: true)]
public List<SalvageMobGroup> MobGroups = default!;
/// <summary>
/// Per expedition type data for this faction.
/// Miscellaneous data for factions.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("configs", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<IFactionExpeditionConfig, SalvageExpeditionPrototype>))]
public Dictionary<string, IFactionExpeditionConfig> Configs = new();
[ViewVariables(VVAccess.ReadWrite), DataField("configs")]
public Dictionary<string, string> Configs = new();
}

View File

@@ -1,17 +0,0 @@
namespace Content.Shared.Salvage.Expeditions.Structure;
/// <summary>
/// Destroy the specified number of structures to finish the expedition.
/// </summary>
[DataDefinition]
public sealed class SalvageStructure : ISalvageMission
{
[DataField("desc")]
public string Description = string.Empty;
[ViewVariables(VVAccess.ReadWrite), DataField("minStructures")]
public int MinStructures = 3;
[ViewVariables(VVAccess.ReadWrite), DataField("maxStructures")]
public int MaxStructures = 5;
}

View File

@@ -1,23 +0,0 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Salvage.Expeditions.Structure;
/// <summary>
/// Per-faction config for Salvage Structure expeditions.
/// </summary>
[DataDefinition]
public sealed class SalvageStructureFaction : IFactionExpeditionConfig
{
/// <summary>
/// Entity prototype of the structures to destroy.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("spawn", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Spawn = default!;
/// <summary>
/// How many groups of mobs to spawn.
/// </summary>
[DataField("groupCount")]
public int Groups = 5;
}

View File

@@ -1,3 +0,0 @@
namespace Content.Shared.Salvage;
public interface ISalvageMission {}

View File

@@ -1,89 +0,0 @@
using Content.Shared.Dataset;
using Content.Shared.Parallax.Biomes;
using Content.Shared.Procedural;
using Content.Shared.Procedural.Loot;
using Content.Shared.Procedural.Rewards;
using Content.Shared.Random;
using Content.Shared.Salvage.Expeditions;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Salvage;
[Prototype("salvageExpedition")]
public sealed class SalvageExpeditionPrototype : IPrototype
{
[IdDataField] public string ID { get; } = default!;
/// <summary>
/// Naming scheme for the FTL marker.
/// </summary>
[DataField("nameProto", customTypeSerializer:typeof(PrototypeIdSerializer<DatasetPrototype>))]
public string NameProto = "names_borer";
/// <summary>
/// Biome to generate the dungeon.
/// </summary>
[DataField("biome", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<BiomePrototype>))]
public string Biome = string.Empty;
/// <summary>
/// Player-friendly description for the console.
/// </summary>
[DataField("desc")]
public string Description = string.Empty;
[DataField("difficultyRating")]
public DifficultyRating DifficultyRating = DifficultyRating.Minor;
// TODO: Make these modifiers but also add difficulty modifiers.
[DataField("light")]
public Color Light = Color.Black;
[DataField("temperature")]
public float Temperature = 293.15f;
[DataField("expedition", required: true)]
public ISalvageMission Mission = default!;
[DataField("minDuration")]
public TimeSpan MinDuration = TimeSpan.FromSeconds(9 * 60);
[DataField("maxDuration")]
public TimeSpan MaxDuration = TimeSpan.FromSeconds(12 * 60);
/// <summary>
/// Available factions for selection for this mission prototype.
/// </summary>
[DataField("factions", customTypeSerializer:typeof(PrototypeIdListSerializer<SalvageFactionPrototype>))]
public List<string> Factions = new();
[DataField("dungeonConfig", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<DungeonConfigPrototype>))]
public string DungeonConfigPrototype = string.Empty;
[DataField("reward", customTypeSerializer: typeof(PrototypeIdSerializer<WeightedRandomPrototype>))]
public string Reward = string.Empty;
/// <summary>
/// Possible loot prototypes available for this expedition.
/// This spawns during the mission and is not tied to completion.
/// </summary>
[DataField("loot", customTypeSerializer: typeof(PrototypeIdListSerializer<WeightedRandomPrototype>))]
public List<string> Loots = new();
[DataField("dungeonPosition")]
public Vector2i DungeonPosition = new(80, -25);
}
[Serializable, NetSerializable]
public enum DifficultyRating : byte
{
None,
Minor,
Moderate,
Hazardous,
Extreme,
}

View File

@@ -1,7 +1,8 @@
using Content.Shared.Salvage.Expeditions;
using Content.Shared.Salvage.Expeditions.Modifiers;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Salvage;
@@ -10,13 +11,15 @@ public sealed class SalvageExpeditionConsoleState : BoundUserInterfaceState
{
public TimeSpan NextOffer;
public bool Claimed;
public bool Cooldown;
public ushort ActiveMission;
public List<SalvageMission> Missions;
public List<SalvageMissionParams> Missions;
public SalvageExpeditionConsoleState(TimeSpan nextOffer, bool claimed, ushort activeMission, List<SalvageMission> missions)
public SalvageExpeditionConsoleState(TimeSpan nextOffer, bool claimed, bool cooldown, ushort activeMission, List<SalvageMissionParams> missions)
{
NextOffer = nextOffer;
Claimed = claimed;
Cooldown = cooldown;
ActiveMission = activeMission;
Missions = missions;
}
@@ -49,6 +52,12 @@ public sealed class SalvageExpeditionDataComponent : Component
[ViewVariables]
public bool Claimed => ActiveMission != 0;
/// <summary>
/// Are we actively cooling down from the last salvage mission.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("cooldown")]
public bool Cooldown = false;
/// <summary>
/// Nexy time salvage missions are offered.
/// </summary>
@@ -56,7 +65,7 @@ public sealed class SalvageExpeditionDataComponent : Component
public TimeSpan NextOffer;
[ViewVariables]
public readonly Dictionary<ushort, SalvageMission> Missions = new();
public readonly Dictionary<ushort, SalvageMissionParams> Missions = new();
[ViewVariables] public ushort ActiveMission;
@@ -64,24 +73,84 @@ public sealed class SalvageExpeditionDataComponent : Component
}
[Serializable, NetSerializable]
public sealed record SalvageMission
public sealed record SalvageMissionParams
{
[ViewVariables]
public ushort Index;
[ViewVariables(VVAccess.ReadWrite), DataField("config", required: true, customTypeSerializer:typeof(SalvageExpeditionPrototype))]
public string Config = default!;
[ViewVariables(VVAccess.ReadWrite)]
public SalvageMissionType MissionType;
[ViewVariables] public TimeSpan Duration;
[ViewVariables(VVAccess.ReadWrite)] public int Seed;
[ViewVariables] public int Seed;
/// <summary>
/// Base difficulty for this mission.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)] public DifficultyRating Difficulty;
}
[Serializable, NetSerializable]
public enum SalvageEnvironment : byte
/// <summary>
/// Created from <see cref="SalvageMissionParams"/>. Only needed for data the client also needs for mission
/// display.
/// </summary>
public sealed record SalvageMission(
int Seed,
DifficultyRating Difficulty,
string Dungeon,
string Faction,
SalvageMissionType Mission,
string Biome,
Color? Color,
TimeSpan Duration,
Dictionary<string, int> Loot,
List<string> Modifiers)
{
Invalid = 0,
Caves,
/// <summary>
/// Seed used for the mission.
/// </summary>
public readonly int Seed = Seed;
/// <summary>
/// Difficulty rating.
/// </summary>
public DifficultyRating Difficulty = Difficulty;
/// <summary>
/// <see cref="SalvageDungeonMod"/> to be used.
/// </summary>
public readonly string Dungeon = Dungeon;
/// <summary>
/// <see cref="SalvageFactionPrototype"/> to be used.
/// </summary>
public readonly string Faction = Faction;
/// <summary>
/// Underlying mission params that generated this.
/// </summary>
public readonly SalvageMissionType Mission = Mission;
/// <summary>
/// Biome to be used for the mission.
/// </summary>
public readonly string Biome = Biome;
/// <summary>
/// Lighting color to be used (AKA outdoor lighting).
/// </summary>
public readonly Color? Color = Color;
/// <summary>
/// Mission duration.
/// </summary>
public TimeSpan Duration = Duration;
public Dictionary<string, int> Loot = Loot;
/// <summary>
/// Modifiers (outside of the above) applied to the mission.
/// </summary>
public List<string> Modifiers = Modifiers;
}
[Serializable, NetSerializable]

View File

@@ -1,77 +1,235 @@
using System.Linq;
using Content.Shared.Dataset;
using Content.Shared.Procedural.Loot;
using Content.Shared.Procedural.Rewards;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Content.Shared.Salvage.Expeditions.Structure;
using Content.Shared.Salvage.Expeditions;
using Content.Shared.Salvage.Expeditions.Modifiers;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.Salvage;
public abstract class SharedSalvageSystem : EntitySystem
{
public static readonly TimeSpan MissionCooldown = TimeSpan.FromMinutes(5);
public static readonly TimeSpan MissionFailedCooldown = TimeSpan.FromMinutes(10);
[Dependency] private readonly ILocalizationManager _loc = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public static float GetDifficultyModifier(DifficultyRating difficulty)
public static readonly TimeSpan MissionCooldown = TimeSpan.FromMinutes(5);
public static readonly TimeSpan MissionFailedCooldown = TimeSpan.FromMinutes(15);
#region Descriptions
public string GetMissionDescription(SalvageMission mission)
{
// These should reflect how many salvage staff are expected to be required for the mission.
switch (difficulty)
// Hardcoded in coooooz it's dynamic based on difficulty and I'm lazy.
switch (mission.Mission)
{
case SalvageMissionType.Mining:
// Taxation: , ("tax", $"{GetMiningTax(mission.Difficulty) * 100f:0}")
return Loc.GetString("salvage-expedition-desc-mining");
case SalvageMissionType.Destruction:
var proto = _proto.Index<SalvageFactionPrototype>(mission.Faction).Configs["DefenseStructure"];
return Loc.GetString("salvage-expedition-desc-structure",
("count", GetStructureCount(mission.Difficulty)),
("structure", _loc.GetEntityData(proto).Name));
default:
throw new NotImplementedException();
}
}
public float GetMiningTax(DifficultyRating baseRating)
{
return 0.6f + (int) baseRating * 0.05f;
}
/// <summary>
/// Gets the amount of structures to destroy.
/// </summary>
public int GetStructureCount(DifficultyRating baseRating)
{
return 1 + (int) baseRating * 2;
}
#endregion
public int GetDifficulty(DifficultyRating rating)
{
switch (rating)
{
case DifficultyRating.None:
return 1f;
return 1;
case DifficultyRating.Minor:
return 1.5f;
return 2;
case DifficultyRating.Moderate:
return 3f;
return 4;
case DifficultyRating.Hazardous:
return 6f;
return 6;
case DifficultyRating.Extreme:
return 10f;
return 8;
default:
throw new ArgumentOutOfRangeException(nameof(difficulty), difficulty, null);
throw new ArgumentOutOfRangeException(nameof(rating), rating, null);
}
}
/// <summary>
/// How many groups of mobs to spawn for a mission.
/// </summary>
public float GetSpawnCount(DifficultyRating difficulty)
{
return (int) difficulty * 2;
}
public static string GetFTLName(DatasetPrototype dataset, int seed)
{
var random = new System.Random(seed);
return $"{dataset.Values[random.Next(dataset.Values.Count)]}-{random.Next(10, 100)}-{(char) (65 + random.Next(26))}";
}
public static string GetFaction(List<string> factions, int seed)
public SalvageMission GetMission(SalvageMissionType config, DifficultyRating difficulty, int seed)
{
var adjustedSeed = new System.Random(seed + 1);
return factions[adjustedSeed.Next(factions.Count)];
// This is on shared to ensure the client display for missions and what the server generates are consistent
var rating = (float) GetDifficulty(difficulty);
// Don't want easy missions to have any negative modifiers but also want
// easy to be a 1 for difficulty.
rating -= 1f;
var rand = new System.Random(seed);
var faction = GetMod<SalvageFactionPrototype>(rand, ref rating);
var biome = GetMod<SalvageBiomeMod>(rand, ref rating);
var dungeon = GetDungeon(biome.ID, rand, ref rating);
var mods = new List<string>();
SalvageLightMod? light = null;
if (biome.BiomePrototype != null)
{
light = GetLight(biome.ID, rand, ref rating);
mods.Add(light.Description);
}
var time = GetMod<SalvageTimeMod>(rand, ref rating);
// Round the duration to nearest 15 seconds.
var exactDuration = time.MinDuration + (time.MaxDuration - time.MinDuration) * rand.NextFloat();
exactDuration = MathF.Round(exactDuration / 15f) * 15f;
var duration = TimeSpan.FromSeconds(exactDuration);
if (time.ID != "StandardTime")
{
mods.Add(time.Description);
}
var loots = GetLoot(config, _proto.EnumeratePrototypes<SalvageLootPrototype>().ToList(), GetDifficulty(difficulty), seed);
return new SalvageMission(seed, difficulty, dungeon.ID, faction.ID, config, biome.ID, light?.Color, duration, loots, mods);
}
public static IEnumerable<SalvageLootPrototype> GetLoot(List<string> loots, int seed, IPrototypeManager protoManager)
public SalvageDungeonMod GetDungeon(string biome, System.Random rand, ref float rating)
{
var mods = _proto.EnumeratePrototypes<SalvageDungeonMod>().ToList();
mods.Sort((x, y) => string.Compare(x.ID, y.ID, StringComparison.Ordinal));
rand.Shuffle(mods);
foreach (var mod in mods)
{
if (mod.BiomeMods?.Contains(biome) == false ||
mod.Cost > rating)
{
continue;
}
rating -= (int) mod.Cost;
return mod;
}
throw new InvalidOperationException();
}
public SalvageLightMod GetLight(string biome, System.Random rand, ref float rating)
{
var mods = _proto.EnumeratePrototypes<SalvageLightMod>().ToList();
mods.Sort((x, y) => string.Compare(x.ID, y.ID, StringComparison.Ordinal));
rand.Shuffle(mods);
foreach (var mod in mods)
{
if (mod.Biomes?.Contains(biome) == false || mod.Cost > rating)
continue;
rating -= mod.Cost;
return mod;
}
throw new InvalidOperationException();
}
public T GetMod<T>(System.Random rand, ref float rating) where T : class, IPrototype, ISalvageMod
{
var mods = _proto.EnumeratePrototypes<T>().ToList();
mods.Sort((x, y) => string.Compare(x.ID, y.ID, StringComparison.Ordinal));
rand.Shuffle(mods);
foreach (var mod in mods)
{
if (mod.Cost > rating)
continue;
rating -= mod.Cost;
return mod;
}
throw new InvalidOperationException();
}
private Dictionary<string, int> GetLoot(SalvageMissionType mission, List<SalvageLootPrototype> loots, int count, int seed)
{
var results = new Dictionary<string, int>();
var adjustedSeed = new System.Random(seed + 2);
for (var i = 0; i < loots.Count; i++)
for (var i = 0; i < count; i++)
{
var loot = loots[i];
var a = protoManager.Index<WeightedRandomPrototype>(loot);
var lootConfig = a.Pick(adjustedSeed);
yield return protoManager.Index<SalvageLootPrototype>(lootConfig);
adjustedSeed.Shuffle(loots);
foreach (var loot in loots)
{
if (loot.Blacklist.Contains(mission))
continue;
var weh = results.GetOrNew(loot.ID);
weh++;
results[loot.ID] = weh;
break;
}
}
return results;
}
public static ISalvageReward GetReward(WeightedRandomPrototype proto, int seed, IPrototypeManager protoManager)
{
var adjustedSeed = new System.Random(seed + 3);
var rewardProto = proto.Pick(adjustedSeed);
return protoManager.Index<SalvageRewardPrototype>(rewardProto).Reward;
}
#region Structure
public static int GetStructureCount(SalvageStructure structure, int seed)
{
var adjustedSeed = new System.Random(seed + 4);
return adjustedSeed.Next(structure.MinStructures, structure.MaxStructures + 1);
}
#endregion
}
[Serializable, NetSerializable]
public enum SalvageMissionType : byte
{
/// <summary>
/// No dungeon, just ore loot and random mob spawns.
/// </summary>
Mining,
/// <summary>
/// Destroy the specified structures in a dungeon.
/// </summary>
Destruction,
}
[Serializable, NetSerializable]
public enum DifficultyRating : byte
{
None,
Minor,
Moderate,
Hazardous,
Extreme,
}