Salvage dungeons (#14520)

This commit is contained in:
metalgearsloth
2023-03-10 16:41:22 +11:00
committed by GitHub
parent 214ca06997
commit 6157dfa3c0
145 changed files with 24649 additions and 396 deletions

View File

@@ -0,0 +1,21 @@
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

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

View File

@@ -0,0 +1,19 @@
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
{
[IdDataField] public string ID { get; } = default!;
[ViewVariables(VVAccess.ReadWrite), DataField("groups", required: true)]
public List<SalvageMobGroup> MobGroups = default!;
/// <summary>
/// Per expedition type data for this faction.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("configs", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<IFactionExpeditionConfig, SalvageExpeditionPrototype>))]
public Dictionary<string, IFactionExpeditionConfig> Configs = new();
}

View File

@@ -0,0 +1,18 @@
using Content.Shared.Storage;
namespace Content.Shared.Salvage.Expeditions;
[DataDefinition]
public record struct SalvageMobGroup()
{
// A mob may be cheap but rare or expensive but frequent.
/// <summary>
/// Probability to spawn this group. Summed with everything else for the faction.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("prob")]
public float Prob = 1f;
[ViewVariables(VVAccess.ReadWrite), DataField("entries", required: true)]
public List<EntitySpawnEntry> Entries = new();
}

View File

@@ -0,0 +1,17 @@
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

@@ -0,0 +1,23 @@
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

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

View File

@@ -0,0 +1,89 @@
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

@@ -0,0 +1,91 @@
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;
[Serializable, NetSerializable]
public sealed class SalvageExpeditionConsoleState : BoundUserInterfaceState
{
public TimeSpan NextOffer;
public bool Claimed;
public ushort ActiveMission;
public List<SalvageMission> Missions;
public SalvageExpeditionConsoleState(TimeSpan nextOffer, bool claimed, ushort activeMission, List<SalvageMission> missions)
{
NextOffer = nextOffer;
Claimed = claimed;
ActiveMission = activeMission;
Missions = missions;
}
}
/// <summary>
/// Used to interact with salvage expeditions and claim them.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class SalvageExpeditionConsoleComponent : Component
{
}
[Serializable, NetSerializable]
public sealed class ClaimSalvageMessage : BoundUserInterfaceMessage
{
public ushort Index;
}
/// <summary>
/// Added per station to store data on their available salvage missions.
/// </summary>
[RegisterComponent]
public sealed class SalvageExpeditionDataComponent : Component
{
/// <summary>
/// Is there an active salvage expedition.
/// </summary>
[ViewVariables]
public bool Claimed => ActiveMission != 0;
/// <summary>
/// Nexy time salvage missions are offered.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("nextOffer", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan NextOffer;
[ViewVariables]
public readonly Dictionary<ushort, SalvageMission> Missions = new();
[ViewVariables] public ushort ActiveMission;
public ushort NextIndex = 1;
}
[Serializable, NetSerializable]
public sealed record SalvageMission
{
[ViewVariables]
public ushort Index;
[ViewVariables(VVAccess.ReadWrite), DataField("config", required: true, customTypeSerializer:typeof(SalvageExpeditionPrototype))]
public string Config = default!;
[ViewVariables] public TimeSpan Duration;
[ViewVariables] public int Seed;
}
[Serializable, NetSerializable]
public enum SalvageEnvironment : byte
{
Invalid = 0,
Caves,
}
[Serializable, NetSerializable]
public enum SalvageConsoleUiKey : byte
{
Expedition,
}

View File

@@ -0,0 +1,77 @@
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 Robust.Shared.Prototypes;
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);
public static float GetDifficultyModifier(DifficultyRating difficulty)
{
// These should reflect how many salvage staff are expected to be required for the mission.
switch (difficulty)
{
case DifficultyRating.None:
return 1f;
case DifficultyRating.Minor:
return 1.5f;
case DifficultyRating.Moderate:
return 3f;
case DifficultyRating.Hazardous:
return 6f;
case DifficultyRating.Extreme:
return 10f;
default:
throw new ArgumentOutOfRangeException(nameof(difficulty), difficulty, null);
}
}
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)
{
var adjustedSeed = new System.Random(seed + 1);
return factions[adjustedSeed.Next(factions.Count)];
}
public static IEnumerable<SalvageLootPrototype> GetLoot(List<string> loots, int seed, IPrototypeManager protoManager)
{
var adjustedSeed = new System.Random(seed + 2);
for (var i = 0; i < loots.Count; i++)
{
var loot = loots[i];
var a = protoManager.Index<WeightedRandomPrototype>(loot);
var lootConfig = a.Pick(adjustedSeed);
yield return protoManager.Index<SalvageLootPrototype>(lootConfig);
}
}
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
}