From 036b9ef74f894b1c4fbd7c67b96a5d929e072ee6 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:52:01 +1000 Subject: [PATCH] Expeditions rework (#18960) --- .../UI/SalvageExpeditionWindow.xaml.cs | 69 +--- .../Procedural/DungeonJob.PostGen.cs | 9 +- .../SalvageEliminationExpeditionComponent.cs | 16 - .../Expeditions/SalvageExpeditionComponent.cs | 12 - .../SalvageMiningExpeditionComponent.cs | 16 - .../SalvageStructureExpeditionComponent.cs | 13 - .../SalvageSystem.ExpeditionConsole.cs | 3 +- .../Salvage/SalvageSystem.Expeditions.cs | 136 +------ .../Salvage/SalvageSystem.Runner.cs | 84 +--- Content.Server/Salvage/SalvageSystem.cs | 2 +- .../Salvage/SpawnSalvageMissionJob.cs | 325 +++++++--------- Content.Shared/CCVar/CCVars.cs | 9 +- .../Procedural/Loot/RandomSpawnsLoot.cs | 33 ++ .../Procedural/Loot/SalvageLootPrototype.cs | 10 - .../Procedural/SalvageDifficultyPrototype.cs | 36 ++ Content.Shared/Random/IBudgetEntry.cs | 20 + Content.Shared/Random/RandomSystem.cs | 58 +++ .../Expeditions/Modifiers/SalvageAirMod.cs | 2 +- ...iomeMod.cs => SalvageBiomeModPrototype.cs} | 2 +- ...onMod.cs => SalvageDungeonModPrototype.cs} | 4 +- .../Expeditions/Modifiers/SalvageLightMod.cs | 2 +- .../Modifiers/SalvageTemperatureMod.cs | 2 +- .../Expeditions/Modifiers/SalvageTimeMod.cs | 23 -- .../Modifiers/SalvageWeatherMod.cs | 2 +- .../Salvage/Expeditions/SalvageExpeditions.cs | 38 +- .../Expeditions/SalvageFactionPrototype.cs | 12 +- .../Salvage/Expeditions/SalvageMobEntry.cs | 24 ++ .../Salvage/Expeditions/SalvageMobGroup.cs | 18 - Content.Shared/Salvage/SharedSalvageSystem.cs | 178 ++------- .../ConfigPresets/Build/development.toml | 1 - .../Locale/en-US/procedural/expeditions.ftl | 21 +- .../Prototypes/Entities/Mobs/NPCs/carp.yml | 9 +- .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 57 ++- .../Entities/Mobs/Player/dragon.yml | 9 +- .../Objects/Weapons/Melee/pickaxe.yml | 12 +- .../Structures/Shuttles/thrusters.yml | 20 + .../Procedural/salvage_difficulties.yml | 12 + .../Procedural/salvage_factions.yml | 80 ++-- .../Prototypes/Procedural/salvage_loot.yml | 128 +++++- .../Prototypes/Procedural/salvage_mods.yml | 364 ++++++++---------- 40 files changed, 774 insertions(+), 1097 deletions(-) delete mode 100644 Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs delete mode 100644 Content.Server/Salvage/Expeditions/SalvageMiningExpeditionComponent.cs delete mode 100644 Content.Server/Salvage/Expeditions/SalvageStructureExpeditionComponent.cs create mode 100644 Content.Shared/Procedural/Loot/RandomSpawnsLoot.cs create mode 100644 Content.Shared/Procedural/SalvageDifficultyPrototype.cs create mode 100644 Content.Shared/Random/IBudgetEntry.cs create mode 100644 Content.Shared/Random/RandomSystem.cs rename Content.Shared/Salvage/Expeditions/Modifiers/{SalvageBiomeMod.cs => SalvageBiomeModPrototype.cs} (92%) rename Content.Shared/Salvage/Expeditions/Modifiers/{SalvageDungeonMod.cs => SalvageDungeonModPrototype.cs} (88%) delete mode 100644 Content.Shared/Salvage/Expeditions/Modifiers/SalvageTimeMod.cs create mode 100644 Content.Shared/Salvage/Expeditions/SalvageMobEntry.cs delete mode 100644 Content.Shared/Salvage/Expeditions/SalvageMobGroup.cs create mode 100644 Resources/Prototypes/Procedural/salvage_difficulties.yml diff --git a/Content.Client/Salvage/UI/SalvageExpeditionWindow.xaml.cs b/Content.Client/Salvage/UI/SalvageExpeditionWindow.xaml.cs index 150f4a2957..7875ac6ef0 100644 --- a/Content.Client/Salvage/UI/SalvageExpeditionWindow.xaml.cs +++ b/Content.Client/Salvage/UI/SalvageExpeditionWindow.xaml.cs @@ -4,6 +4,7 @@ using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; using Content.Shared.CCVar; using Content.Shared.Parallax.Biomes; +using Content.Shared.Procedural; using Content.Shared.Salvage; using Content.Shared.Salvage.Expeditions; using Content.Shared.Salvage.Expeditions.Modifiers; @@ -53,8 +54,10 @@ public sealed partial class SalvageExpeditionWindow : FancyWindow, for (var i = 0; i < state.Missions.Count; i++) { var missionParams = state.Missions[i]; - var config = missionParams.MissionType; - var mission = _salvage.GetMission(missionParams.MissionType, missionParams.Difficulty, missionParams.Seed); + var difficultyId = "Moderate"; + var difficultyProto = _prototype.Index(difficultyId); + // TODO: Selectable difficulty soon. + var mission = _salvage.GetMission(difficultyProto, missionParams.Seed); // Mission title var missionStripe = new StripeBack() @@ -64,7 +67,7 @@ public sealed partial class SalvageExpeditionWindow : FancyWindow, missionStripe.AddChild(new Label() { - Text = Loc.GetString($"salvage-expedition-type-{config.ToString()}"), + Text = Loc.GetString($"salvage-expedition-type"), HorizontalAlignment = HAlignment.Center, Margin = new Thickness(0f, 5f, 0f, 5f), }); @@ -81,48 +84,25 @@ public sealed partial class SalvageExpeditionWindow : FancyWindow, Text = Loc.GetString("salvage-expedition-window-difficulty") }); - Color difficultyColor; - - switch (missionParams.Difficulty) - { - case DifficultyRating.Minimal: - difficultyColor = Color.FromHex("#52B4E996"); - break; - case DifficultyRating.Minor: - difficultyColor = Color.FromHex("#9FED5896"); - break; - case DifficultyRating.Moderate: - difficultyColor = Color.FromHex("#EFB34196"); - break; - case DifficultyRating.Hazardous: - difficultyColor = Color.FromHex("#DE3A3A96"); - break; - case DifficultyRating.Extreme: - difficultyColor = Color.FromHex("#D381C996"); - break; - default: - throw new ArgumentOutOfRangeException(); - } + var difficultyColor = difficultyProto.Color; lBox.AddChild(new Label { - Text = Loc.GetString($"salvage-expedition-difficulty-{missionParams.Difficulty.ToString()}"), + Text = Loc.GetString("salvage-expedition-difficulty-Moderate"), FontColorOverride = difficultyColor, HorizontalAlignment = HAlignment.Left, Margin = new Thickness(0f, 0f, 0f, 5f), }); - // Details - var details = _salvage.GetMissionDescription(mission); - lBox.AddChild(new Label { - Text = Loc.GetString("salvage-expedition-window-details") + Text = Loc.GetString("salvage-expedition-difficulty-players"), + HorizontalAlignment = HAlignment.Left, }); lBox.AddChild(new Label { - Text = details, + Text = difficultyProto.RecommendedPlayers.ToString(), FontColorOverride = StyleNano.NanoGold, HorizontalAlignment = HAlignment.Left, Margin = new Thickness(0f, 0f, 0f, 5f), @@ -168,7 +148,7 @@ public sealed partial class SalvageExpeditionWindow : FancyWindow, lBox.AddChild(new Label { - Text = Loc.GetString(_prototype.Index(biome).ID), + Text = Loc.GetString(_prototype.Index(biome).ID), FontColorOverride = StyleNano.NanoGold, HorizontalAlignment = HAlignment.Left, Margin = new Thickness(0f, 0f, 0f, 5f), @@ -190,29 +170,6 @@ public sealed partial class SalvageExpeditionWindow : FancyWindow, Margin = new Thickness(0f, 0f, 0f, 5f), }); - lBox.AddChild(new Label() - { - Text = Loc.GetString("salvage-expedition-window-rewards") - }); - - var rewards = new Dictionary(); - foreach (var reward in mission.Rewards) - { - var name = _prototype.Index(reward).Name; - var count = rewards.GetOrNew(name); - count++; - rewards[name] = count; - } - - // there will always be 3 or more rewards so no need for 0 check - lBox.AddChild(new Label() - { - Text = string.Join("\n", rewards.Select(o => "- " + o.Key + (o.Value > 1 ? $" x {o.Value}" : ""))).TrimEnd(), - FontColorOverride = StyleNano.ConcerningOrangeFore, - HorizontalAlignment = HAlignment.Left, - Margin = new Thickness(0f, 0f, 0f, 5f) - }); - // Claim var claimButton = new Button() { @@ -289,7 +246,7 @@ public sealed partial class SalvageExpeditionWindow : FancyWindow, else { var cooldown = _cooldown - ? TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.SalvageExpeditionFailedCooldown)) + ? TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.SalvageExpeditionCooldown)) : TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.SalvageExpeditionCooldown)); NextOfferBar.Value = 1f - (float) (remaining / cooldown); diff --git a/Content.Server/Procedural/DungeonJob.PostGen.cs b/Content.Server/Procedural/DungeonJob.PostGen.cs index 1bbcc438ef..4718de7c0c 100644 --- a/Content.Server/Procedural/DungeonJob.PostGen.cs +++ b/Content.Server/Procedural/DungeonJob.PostGen.cs @@ -83,17 +83,14 @@ public sealed partial class DungeonJob var lastDirection = new Dictionary(); costSoFar[start] = 0f; lastDirection[start] = Direction.Invalid; - var tagQuery = _entManager.GetEntityQuery(); - // TODO: - // Pick a random node to start - // Then, dijkstra out from it. Add like +10 if it's a wall or smth - // When we hit another cable then mark it as found and iterate cameFrom and add to the thingie. while (remaining.Count > 0) { if (frontier.Count == 0) { - frontier.Enqueue(remaining.First(), 0f); + var newStart = remaining.First(); + frontier.Enqueue(newStart, 0f); + lastDirection[newStart] = Direction.Invalid; } var node = frontier.Dequeue(); diff --git a/Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs b/Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs deleted file mode 100644 index a3ec66ff30..0000000000 --- a/Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Shared.Salvage; - -namespace Content.Server.Salvage.Expeditions.Structure; - -/// -/// Tracks expedition data for -/// -[RegisterComponent, Access(typeof(SalvageSystem), typeof(SpawnSalvageMissionJob))] -public sealed partial class SalvageEliminationExpeditionComponent : Component -{ - /// - /// List of mobs that need to be killed for the mission to be complete. - /// - [DataField("megafauna")] - public List Megafauna = new(); -} diff --git a/Content.Server/Salvage/Expeditions/SalvageExpeditionComponent.cs b/Content.Server/Salvage/Expeditions/SalvageExpeditionComponent.cs index ee4b7c88cd..2bc00397bc 100644 --- a/Content.Server/Salvage/Expeditions/SalvageExpeditionComponent.cs +++ b/Content.Server/Salvage/Expeditions/SalvageExpeditionComponent.cs @@ -49,16 +49,4 @@ public sealed partial class SalvageExpeditionComponent : SharedSalvageExpedition { Params = AudioParams.Default.WithVolume(-5), }; - - /// - /// The difficulty this mission had or, in the future, was selected. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("difficulty")] - public DifficultyRating Difficulty; - - /// - /// List of items to order on mission completion - /// - [ViewVariables(VVAccess.ReadWrite), DataField("rewards", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List Rewards = default!; } diff --git a/Content.Server/Salvage/Expeditions/SalvageMiningExpeditionComponent.cs b/Content.Server/Salvage/Expeditions/SalvageMiningExpeditionComponent.cs deleted file mode 100644 index b9a1379aab..0000000000 --- a/Content.Server/Salvage/Expeditions/SalvageMiningExpeditionComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Shared.Salvage; - -namespace Content.Server.Salvage.Expeditions; - -/// -/// Tracks expedition data for -/// -[RegisterComponent, Access(typeof(SalvageSystem))] -public sealed partial class SalvageMiningExpeditionComponent : Component -{ - /// - /// Entities that were present on the shuttle and match the loot tax. - /// - [DataField("exemptEntities")] - public List ExemptEntities = new(); -} diff --git a/Content.Server/Salvage/Expeditions/SalvageStructureExpeditionComponent.cs b/Content.Server/Salvage/Expeditions/SalvageStructureExpeditionComponent.cs deleted file mode 100644 index 0dec1f81ad..0000000000 --- a/Content.Server/Salvage/Expeditions/SalvageStructureExpeditionComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Content.Shared.Salvage; - -namespace Content.Server.Salvage.Expeditions.Structure; - -/// -/// Tracks expedition data for -/// -[RegisterComponent, Access(typeof(SalvageSystem), typeof(SpawnSalvageMissionJob))] -public sealed partial class SalvageStructureExpeditionComponent : Component -{ - [DataField("structures")] - public List Structures = new(); -} diff --git a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs index c814e9c36b..d4241b2baf 100644 --- a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs +++ b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs @@ -1,3 +1,4 @@ +using Content.Shared.Procedural; using Content.Shared.Salvage; using Content.Shared.Salvage.Expeditions; @@ -18,7 +19,7 @@ public sealed partial class SalvageSystem SpawnMission(missionparams, station.Value); data.ActiveMission = args.Index; - var mission = GetMission(missionparams.MissionType, missionparams.Difficulty, missionparams.Seed); + var mission = GetMission(_prototypeManager.Index(missionparams.Difficulty), missionparams.Seed); data.NextOffer = _timing.CurTime + mission.Duration + TimeSpan.FromSeconds(1); UpdateConsoles(data); } diff --git a/Content.Server/Salvage/SalvageSystem.Expeditions.cs b/Content.Server/Salvage/SalvageSystem.Expeditions.cs index a1ac37062b..6021cb6236 100644 --- a/Content.Server/Salvage/SalvageSystem.Expeditions.cs +++ b/Content.Server/Salvage/SalvageSystem.Expeditions.cs @@ -8,6 +8,7 @@ using Robust.Shared.CPUJob.JobQueues; using Robust.Shared.CPUJob.JobQueues.Queues; using System.Linq; using System.Threading; +using Content.Shared.Procedural; using Content.Shared.Salvage.Expeditions; using Robust.Shared.GameStates; @@ -26,7 +27,6 @@ public sealed partial class SalvageSystem private const double SalvageJobTime = 0.002; private float _cooldown; - private float _failedCooldown; private void InitializeExpeditions() { @@ -43,9 +43,7 @@ public sealed partial class SalvageSystem SubscribeLocalEvent(OnStructureExamine); _cooldown = _configurationManager.GetCVar(CCVars.SalvageExpeditionCooldown); - _failedCooldown = _configurationManager.GetCVar(CCVars.SalvageExpeditionFailedCooldown); _configurationManager.OnValueChanged(CCVars.SalvageExpeditionCooldown, SetCooldownChange); - _configurationManager.OnValueChanged(CCVars.SalvageExpeditionFailedCooldown, SetFailedCooldownChange); } private void OnExpeditionGetState(EntityUid uid, SalvageExpeditionComponent component, ref ComponentGetState args) @@ -59,7 +57,6 @@ public sealed partial class SalvageSystem private void ShutdownExpeditions() { _configurationManager.UnsubValueChanged(CCVars.SalvageExpeditionCooldown, SetCooldownChange); - _configurationManager.UnsubValueChanged(CCVars.SalvageExpeditionFailedCooldown, SetFailedCooldownChange); } private void SetCooldownChange(float obj) @@ -77,20 +74,6 @@ public sealed partial class SalvageSystem _cooldown = obj; } - private void SetFailedCooldownChange(float obj) - { - var diff = obj - _failedCooldown; - - var query = AllEntityQuery(); - - while (query.MoveNext(out var comp)) - { - comp.NextOffer += TimeSpan.FromSeconds(diff); - } - - _failedCooldown = obj; - } - private void OnExpeditionShutdown(EntityUid uid, SalvageExpeditionComponent component, ComponentShutdown args) { component.Stream?.Stop(); @@ -110,7 +93,7 @@ public sealed partial class SalvageSystem // Finish mission if (TryComp(component.Station, out var data)) { - FinishExpedition(data, uid, component, null); + FinishExpedition(data, uid); } } @@ -152,109 +135,29 @@ public sealed partial class SalvageSystem } } - private void FinishExpedition(SalvageExpeditionDataComponent component, EntityUid uid, SalvageExpeditionComponent expedition, EntityUid? shuttle) + private void FinishExpedition(SalvageExpeditionDataComponent component, EntityUid uid) { - // Finish mission cleanup. - switch (expedition.MissionParams.MissionType) - { - // Handles the mining taxation. - case SalvageMissionType.Mining: - expedition.Completed = true; - - if (shuttle != null && TryComp(uid, out var mining)) - { - var xformQuery = GetEntityQuery(); - var entities = new List(); - MiningTax(entities, shuttle.Value, mining, xformQuery); - - var tax = GetMiningTax(expedition.MissionParams.Difficulty); - _random.Shuffle(entities); - - // TODO: urgh this pr is already taking so long I'll do this later - for (var i = 0; i < Math.Ceiling(entities.Count * tax); i++) - { - // QueueDel(entities[i]); - } - } - - break; - } - - // Handle payout after expedition has finished - if (expedition.Completed) - { - Log.Debug($"Completed mission {expedition.MissionParams.MissionType} with seed {expedition.MissionParams.Seed}"); - component.NextOffer = _timing.CurTime + TimeSpan.FromSeconds(_cooldown); - Announce(uid, Loc.GetString("salvage-expedition-mission-completed")); - GiveRewards(expedition); - } - else - { - Log.Debug($"Failed mission {expedition.MissionParams.MissionType} with seed {expedition.MissionParams.Seed}"); - component.NextOffer = _timing.CurTime + TimeSpan.FromSeconds(_failedCooldown); - Announce(uid, Loc.GetString("salvage-expedition-mission-failed")); - } - + component.NextOffer = _timing.CurTime + TimeSpan.FromSeconds(_cooldown); + Announce(uid, Loc.GetString("salvage-expedition-mission-completed")); component.ActiveMission = 0; component.Cooldown = true; UpdateConsoles(component); } - /// - /// Deducts ore tax for mining. - /// - private void MiningTax(List entities, EntityUid entity, SalvageMiningExpeditionComponent mining, EntityQuery xformQuery) - { - if (!mining.ExemptEntities.Contains(entity)) - { - entities.Add(entity); - } - - var xform = xformQuery.GetComponent(entity); - var children = xform.ChildEnumerator; - - while (children.MoveNext(out var child)) - { - MiningTax(entities, child.Value, mining, xformQuery); - } - } - private void GenerateMissions(SalvageExpeditionDataComponent component) { component.Missions.Clear(); - var configs = Enum.GetValues().ToList(); - - // Temporarily removed coz it SUCKS - configs.Remove(SalvageMissionType.Mining); - - // this doesn't support having more missions than types of ratings - // but the previous system didn't do that either. - var allDifficulties = Enum.GetValues(); - _random.Shuffle(allDifficulties); - var difficulties = allDifficulties.Take(MissionLimit).ToList(); - difficulties.Sort(); - - if (configs.Count == 0) - return; for (var i = 0; i < MissionLimit; i++) { - _random.Shuffle(configs); - var rating = difficulties[i]; - - foreach (var config in configs) + var mission = new SalvageMissionParams { - var mission = new SalvageMissionParams - { - Index = component.NextIndex, - MissionType = config, - Seed = _random.Next(), - Difficulty = rating, - }; + Index = component.NextIndex, + Seed = _random.Next(), + Difficulty = "Moderate", + }; - component.Missions[component.NextIndex++] = mission; - break; - } + component.Missions[component.NextIndex++] = mission; } } @@ -271,13 +174,13 @@ public sealed partial class SalvageSystem SalvageJobTime, EntityManager, _timing, + _logManager, _mapManager, _prototypeManager, _anchorable, _biome, _dungeon, _metaData, - this, station, missionParams, cancelToken.Token); @@ -290,19 +193,4 @@ public sealed partial class SalvageSystem { args.PushMarkup(Loc.GetString("salvage-expedition-structure-examine")); } - - private void GiveRewards(SalvageExpeditionComponent comp) - { - // send it to cargo, no rewards otherwise. - if (!TryComp(comp.Station, out var cargoDb)) - return; - - foreach (var reward in comp.Rewards) - { - var sender = Loc.GetString("cargo-gift-default-sender"); - var desc = Loc.GetString("salvage-expedition-reward-description"); - var dest = Loc.GetString("cargo-gift-default-dest"); - _cargo.AddAndApproveOrder(comp.Station, reward, 0, 1, sender, desc, dest, cargoDb); - } - } } diff --git a/Content.Server/Salvage/SalvageSystem.Runner.cs b/Content.Server/Salvage/SalvageSystem.Runner.cs index ce65ead928..0863362131 100644 --- a/Content.Server/Salvage/SalvageSystem.Runner.cs +++ b/Content.Server/Salvage/SalvageSystem.Runner.cs @@ -43,7 +43,7 @@ public sealed partial class SalvageSystem // TODO: This is terrible but need bluespace harnesses or something. var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var _, out var mobState, out var mobXform)) + while (query.MoveNext(out var uid, out _, out var mobState, out var mobXform)) { if (mobXform.MapUid != xform.MapUid) continue; @@ -109,22 +109,11 @@ public sealed partial class SalvageSystem Announce(args.MapUid, Loc.GetString("salvage-expedition-announcement-dungeon", ("direction", component.DungeonLocation.GetDir()))); component.Stage = ExpeditionStage.Running; - Dirty(component); + Dirty(args.MapUid, component); } private void OnFTLStarted(ref FTLStartedEvent ev) { - // Started a mining mission so work out exempt entities - if (TryComp( - _mapManager.GetMapEntityId(ev.TargetCoordinates.ToMap(EntityManager, _transform).MapId), - out var mining)) - { - var ents = new List(); - var xformQuery = GetEntityQuery(); - MiningTax(ents, ev.Entity, mining, xformQuery); - mining.ExemptEntities = ents; - } - if (!TryComp(ev.FromMapUid, out var expedition) || !TryComp(expedition.Station, out var station)) { @@ -169,7 +158,7 @@ public sealed partial class SalvageSystem Dirty(uid, comp); Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(2).Minutes))); } - else if (comp.Stage < ExpeditionStage.Countdown && remaining < TimeSpan.FromMinutes(5)) + else if (comp.Stage < ExpeditionStage.Countdown && remaining < TimeSpan.FromMinutes(4)) { comp.Stage = ExpeditionStage.Countdown; Dirty(uid, comp); @@ -210,72 +199,5 @@ public sealed partial class SalvageSystem QueueDel(uid); } } - - // Mining missions: NOOP since it's handled after ftling - - // Structure missions - var structureQuery = EntityQueryEnumerator(); - - while (structureQuery.MoveNext(out var uid, out var structure, out var comp)) - { - if (comp.Completed) - continue; - - var structureAnnounce = false; - - for (var i = 0; i < structure.Structures.Count; i++) - { - var objective = structure.Structures[i]; - - if (Deleted(objective)) - { - structure.Structures.RemoveSwap(i); - structureAnnounce = true; - } - } - - if (structureAnnounce) - { - Announce(uid, Loc.GetString("salvage-expedition-structure-remaining", ("count", structure.Structures.Count))); - } - - if (structure.Structures.Count == 0) - { - comp.Completed = true; - Announce(uid, Loc.GetString("salvage-expedition-completed")); - } - } - - // Elimination missions - var eliminationQuery = EntityQueryEnumerator(); - while (eliminationQuery.MoveNext(out var uid, out var elimination, out var comp)) - { - if (comp.Completed) - continue; - - var announce = false; - - for (var i = 0; i < elimination.Megafauna.Count; i++) - { - var mob = elimination.Megafauna[i]; - - if (Deleted(mob) || _mobState.IsDead(mob)) - { - elimination.Megafauna.RemoveSwap(i); - announce = true; - } - } - - if (announce) - { - Announce(uid, Loc.GetString("salvage-expedition-megafauna-remaining", ("count", elimination.Megafauna.Count))); - } - - if (elimination.Megafauna.Count == 0) - { - comp.Completed = true; - Announce(uid, Loc.GetString("salvage-expedition-completed")); - } - } } } diff --git a/Content.Server/Salvage/SalvageSystem.cs b/Content.Server/Salvage/SalvageSystem.cs index 1065f7426e..0da6207289 100644 --- a/Content.Server/Salvage/SalvageSystem.cs +++ b/Content.Server/Salvage/SalvageSystem.cs @@ -37,12 +37,12 @@ namespace Content.Server.Salvage [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AnchorableSystem _anchorable = default!; [Dependency] private readonly BiomeSystem _biome = default!; - [Dependency] private readonly CargoSystem _cargo = default!; [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index c6b71b4095..f38114cc44 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -1,3 +1,4 @@ +using System.Collections; using System.Linq; using System.Numerics; using System.Threading; @@ -19,6 +20,7 @@ using Content.Shared.Parallax.Biomes; using Content.Shared.Physics; using Content.Shared.Procedural; using Content.Shared.Procedural.Loot; +using Content.Shared.Random; using Content.Shared.Salvage; using Content.Shared.Salvage.Expeditions; using Content.Shared.Salvage.Expeditions.Modifiers; @@ -29,6 +31,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; +using Robust.Shared.Utility; namespace Content.Server.Salvage; @@ -42,22 +45,23 @@ public sealed class SpawnSalvageMissionJob : Job private readonly BiomeSystem _biome; private readonly DungeonSystem _dungeon; private readonly MetaDataSystem _metaData; - private readonly SalvageSystem _salvage; public readonly EntityUid Station; private readonly SalvageMissionParams _missionParams; + private readonly ISawmill _sawmill; + public SpawnSalvageMissionJob( double maxTime, IEntityManager entManager, IGameTiming timing, + ILogManager logManager, IMapManager mapManager, IPrototypeManager protoManager, AnchorableSystem anchorable, BiomeSystem biome, DungeonSystem dungeon, MetaDataSystem metaData, - SalvageSystem salvage, EntityUid station, SalvageMissionParams missionParams, CancellationToken cancellation = default) : base(maxTime, cancellation) @@ -70,15 +74,17 @@ public sealed class SpawnSalvageMissionJob : Job _biome = biome; _dungeon = dungeon; _metaData = metaData; - _salvage = salvage; Station = station; _missionParams = missionParams; + _sawmill = logManager.GetSawmill("salvage_job"); +#if !DEBUG + _sawmill.Level = LogLevel.Info; +#endif } protected override async Task Process() { - Logger.DebugS("salvage", $"Spawning salvage mission with seed {_missionParams.Seed}"); - var config = _missionParams.MissionType; + _sawmill.Debug("salvage", $"Spawning salvage mission with seed {_missionParams.Seed}"); var mapId = _mapManager.CreateMap(); var mapUid = _mapManager.GetMapEntityId(mapId); _mapManager.AddUninitializedMap(mapId); @@ -88,16 +94,17 @@ public sealed class SpawnSalvageMissionJob : Job // Setup mission configs // As we go through the config the rating will deplete so we'll go for most important to least important. + var difficultyId = "Moderate"; + var difficultyProto = _prototypeManager.Index(difficultyId); var mission = _entManager.System() - .GetMission(_missionParams.MissionType, _missionParams.Difficulty, _missionParams.Seed); + .GetMission(difficultyProto, _missionParams.Seed); - var missionBiome = _prototypeManager.Index(mission.Biome); - BiomeComponent? biome = null; + var missionBiome = _prototypeManager.Index(mission.Biome); if (missionBiome.BiomePrototype != null) { - biome = _entManager.AddComponent(mapUid); + var biome = _entManager.AddComponent(mapUid); var biomeSystem = _entManager.System(); biomeSystem.SetTemplate(biome, _prototypeManager.Index(missionBiome.BiomePrototype)); biomeSystem.SetSeed(biome, mission.Seed); @@ -125,7 +132,7 @@ public sealed class SpawnSalvageMissionJob : Job { var lighting = _entManager.EnsureComponent(mapUid); lighting.AmbientLightColor = mission.Color.Value; - _entManager.Dirty(lighting); + _entManager.Dirty(mapUid, lighting); } } @@ -137,8 +144,6 @@ public sealed class SpawnSalvageMissionJob : Job expedition.Station = Station; expedition.EndTime = _timing.CurTime + mission.Duration; expedition.MissionParams = _missionParams; - expedition.Difficulty = _missionParams.Difficulty; - expedition.Rewards = mission.Rewards; // Don't want consoles to have the incorrect name until refreshed. var ftlUid = _entManager.CreateEntityUninitialized("FTLPoint", new EntityCoordinates(mapUid, grid.TileSizeHalfVector)); @@ -151,29 +156,23 @@ public sealed class SpawnSalvageMissionJob : Job // We'll use the dungeon rotation as the spawn angle var dungeonRotation = _dungeon.GetDungeonRotation(_missionParams.Seed); - Dungeon dungeon = default!; + var maxDungeonOffset = minDungeonOffset + 12; + var dungeonOffsetDistance = minDungeonOffset + (maxDungeonOffset - minDungeonOffset) * random.NextFloat(); + var dungeonOffset = new Vector2(0f, dungeonOffsetDistance); + dungeonOffset = dungeonRotation.RotateVec(dungeonOffset); + var dungeonMod = _prototypeManager.Index(mission.Dungeon); + var dungeonConfig = _prototypeManager.Index(dungeonMod.Proto); + var dungeon = await WaitAsyncTask(_dungeon.GenerateDungeonAsync(dungeonConfig, mapUid, grid, (Vector2i) dungeonOffset, + _missionParams.Seed)); - if (config != SalvageMissionType.Mining) + // Aborty + if (dungeon.Rooms.Count == 0) { - var maxDungeonOffset = minDungeonOffset + 12; - var dungeonOffsetDistance = minDungeonOffset + (maxDungeonOffset - minDungeonOffset) * random.NextFloat(); - var dungeonOffset = new Vector2(0f, dungeonOffsetDistance); - dungeonOffset = dungeonRotation.RotateVec(dungeonOffset); - var dungeonMod = _prototypeManager.Index(mission.Dungeon); - var dungeonConfig = _prototypeManager.Index(dungeonMod.Proto); - dungeon = - await WaitAsyncTask(_dungeon.GenerateDungeonAsync(dungeonConfig, mapUid, grid, (Vector2i) dungeonOffset, - _missionParams.Seed)); - - // Aborty - if (dungeon.Rooms.Count == 0) - { - return false; - } - - expedition.DungeonLocation = dungeonOffset; + return false; } + expedition.DungeonLocation = dungeonOffset; + List reservedTiles = new(); foreach (var tile in grid.GetTilesIntersecting(new Circle(Vector2.Zero, landingPadRadius), false)) @@ -184,24 +183,14 @@ public sealed class SpawnSalvageMissionJob : Job reservedTiles.Add(tile.GridIndices); } - // Mission setup - switch (config) - { - case SalvageMissionType.Mining: - await SetupMining(mission, mapUid); - break; - case SalvageMissionType.Destruction: - await SetupStructure(mission, dungeon, mapUid, grid, random); - break; - case SalvageMissionType.Elimination: - await SetupElimination(mission, dungeon, mapUid, grid, random); - break; - default: - throw new NotImplementedException(); - } + var budgetEntries = new List(); + + /* + * GUARANTEED LOOT + */ - // Handle loot // We'll always add this loot if possible + // mainly used for ore layers. foreach (var lootProto in _prototypeManager.EnumeratePrototypes()) { if (!lootProto.Guaranteed) @@ -210,10 +199,104 @@ public sealed class SpawnSalvageMissionJob : Job await SpawnDungeonLoot(dungeon, missionBiome, lootProto, mapUid, grid, random, reservedTiles); } + // Handle boss loot (when relevant). + + // Handle mob loot. + + // Handle remaining loot + + /* + * MOB SPAWNS + */ + + var mobBudget = difficultyProto.MobBudget; + var faction = _prototypeManager.Index(mission.Faction); + var randomSystem = _entManager.System(); + + foreach (var entry in faction.MobGroups) + { + budgetEntries.Add(entry); + } + + var probSum = budgetEntries.Sum(x => x.Prob); + + while (mobBudget > 0f) + { + var entry = randomSystem.GetBudgetEntry(ref mobBudget, ref probSum, budgetEntries, random); + if (entry == null) + break; + + await SpawnRandomEntry(grid, entry, dungeon, random); + } + + var allLoot = _prototypeManager.Index(SharedSalvageSystem.ExpeditionsLootProto); + var lootBudget = difficultyProto.LootBudget; + + foreach (var rule in allLoot.LootRules) + { + switch (rule) + { + case RandomSpawnsLoot randomLoot: + budgetEntries.Clear(); + + foreach (var entry in randomLoot.Entries) + { + budgetEntries.Add(entry); + } + + probSum = budgetEntries.Sum(x => x.Prob); + + while (lootBudget > 0f) + { + var entry = randomSystem.GetBudgetEntry(ref lootBudget, ref probSum, budgetEntries, random); + if (entry == null) + break; + + _sawmill.Debug($"Spawning dungeon loot {entry.Proto}"); + await SpawnRandomEntry(grid, entry, dungeon, random); + } + break; + default: + throw new NotImplementedException(); + } + } + return true; } - private async Task SpawnDungeonLoot(Dungeon? dungeon, SalvageBiomeMod biomeMod, SalvageLootPrototype loot, EntityUid gridUid, MapGridComponent grid, Random random, List reservedTiles) + private async Task SpawnRandomEntry(MapGridComponent grid, IBudgetEntry entry, Dungeon dungeon, Random random) + { + await SuspendIfOutOfTime(); + + var availableRooms = new ValueList(dungeon.Rooms); + var availableTiles = new List(); + + while (availableRooms.Count > 0) + { + availableTiles.Clear(); + var roomIndex = random.Next(availableRooms.Count); + var room = availableRooms.RemoveSwap(roomIndex); + availableTiles.AddRange(room.Tiles); + + while (availableTiles.Count > 0) + { + var tile = availableTiles.RemoveSwap(random.Next(availableTiles.Count)); + + if (!_anchorable.TileFree(grid, tile, (int) CollisionGroup.MachineLayer, + (int) CollisionGroup.MachineLayer)) + { + continue; + } + + _entManager.SpawnAtPosition(entry.Proto, grid.GridTileToLocal(tile)); + return; + } + } + + // oh noooooooooooo + } + + private async Task SpawnDungeonLoot(Dungeon dungeon, SalvageBiomeModPrototype biomeMod, SalvageLootPrototype loot, EntityUid gridUid, MapGridComponent grid, Random random, List reservedTiles) { for (var i = 0; i < loot.LootRules.Count; i++) { @@ -241,150 +324,4 @@ public sealed class SpawnSalvageMissionJob : Job } } } - - #region Mission Specific - - private async Task SetupMining( - SalvageMission mission, - EntityUid gridUid) - { - var faction = _prototypeManager.Index(mission.Faction); - - if (_entManager.TryGetComponent(gridUid, out var biome)) - { - // TODO: Better - for (var i = 0; i < _salvage.GetDifficulty(mission.Difficulty); i++) - { - _biome.AddMarkerLayer(biome, faction.Configs["Mining"]); - } - } - } - - private async Task SetupStructure( - SalvageMission mission, - Dungeon dungeon, - EntityUid gridUid, - MapGridComponent grid, - Random random) - { - var structureComp = _entManager.EnsureComponent(gridUid); - var availableRooms = dungeon.Rooms.ToList(); - var faction = _prototypeManager.Index(mission.Faction); - await SpawnMobsRandomRooms(mission, dungeon, faction, grid, random); - - var structureCount = _salvage.GetStructureCount(mission.Difficulty); - var shaggy = faction.Configs["DefenseStructure"]; - var validSpawns = new List(); - - // Spawn the objectives - for (var i = 0; i < structureCount; i++) - { - var structureRoom = availableRooms[random.Next(availableRooms.Count)]; - validSpawns.Clear(); - validSpawns.AddRange(structureRoom.Tiles); - random.Shuffle(validSpawns); - - while (validSpawns.Count > 0) - { - var spawnTile = validSpawns[^1]; - validSpawns.RemoveAt(validSpawns.Count - 1); - - if (!_anchorable.TileFree(grid, spawnTile, (int) CollisionGroup.MachineLayer, - (int) CollisionGroup.MachineLayer)) - { - continue; - } - - var spawnPosition = grid.GridTileToLocal(spawnTile); - var uid = _entManager.SpawnEntity(shaggy, spawnPosition); - _entManager.AddComponent(uid); - structureComp.Structures.Add(uid); - break; - } - } - } - - private async Task SetupElimination( - SalvageMission mission, - Dungeon dungeon, - EntityUid gridUid, - MapGridComponent grid, - Random random) - { - // spawn megafauna in a random place - var roomIndex = random.Next(dungeon.Rooms.Count); - var room = dungeon.Rooms[roomIndex]; - var tile = room.Tiles.ElementAt(random.Next(room.Tiles.Count)); - var position = grid.GridTileToLocal(tile); - - var faction = _prototypeManager.Index(mission.Faction); - var prototype = faction.Configs["Megafauna"]; - var uid = _entManager.SpawnEntity(prototype, position); - // not removing ghost role since its 1 megafauna, expect that you won't be able to cheese it. - var eliminationComp = _entManager.EnsureComponent(gridUid); - eliminationComp.Megafauna.Add(uid); - - // spawn less mobs than usual since there's megafauna to deal with too - await SpawnMobsRandomRooms(mission, dungeon, faction, grid, random, 0.5f); - } - - private async Task SpawnMobsRandomRooms(SalvageMission mission, Dungeon dungeon, SalvageFactionPrototype faction, MapGridComponent grid, Random random, float scale = 1f) - { - // scale affects how many groups are spawned, not the size of the groups themselves - var groupSpawns = _salvage.GetSpawnCount(mission.Difficulty) * scale; - var groupSum = faction.MobGroups.Sum(o => o.Prob); - var validSpawns = new List(); - - for (var i = 0; i < groupSpawns; i++) - { - var roll = random.NextFloat() * groupSum; - var value = 0f; - - foreach (var group in faction.MobGroups) - { - value += group.Prob; - - if (value < roll) - continue; - - var mobGroupIndex = random.Next(faction.MobGroups.Count); - var mobGroup = faction.MobGroups[mobGroupIndex]; - - var spawnRoomIndex = random.Next(dungeon.Rooms.Count); - var spawnRoom = dungeon.Rooms[spawnRoomIndex]; - validSpawns.Clear(); - validSpawns.AddRange(spawnRoom.Tiles); - random.Shuffle(validSpawns); - - foreach (var entry in EntitySpawnCollection.GetSpawns(mobGroup.Entries, random)) - { - while (validSpawns.Count > 0) - { - var spawnTile = validSpawns[^1]; - validSpawns.RemoveAt(validSpawns.Count - 1); - - if (!_anchorable.TileFree(grid, spawnTile, (int) CollisionGroup.MachineLayer, - (int) CollisionGroup.MachineLayer)) - { - continue; - } - - var spawnPosition = grid.GridTileToLocal(spawnTile); - - var uid = _entManager.CreateEntityUninitialized(entry, spawnPosition); - _entManager.RemoveComponent(uid); - _entManager.RemoveComponent(uid); - _entManager.InitializeAndStartEntity(uid); - - break; - } - } - - await SuspendIfOutOfTime(); - break; - } - } - } - - #endregion } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index a0cde055ba..ccc8b6d51d 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1500,13 +1500,16 @@ namespace Content.Shared.CCVar SalvageForced = CVarDef.Create("salvage.forced", "", CVar.SERVERONLY); /// - /// Cooldown for successful missions. + /// Duration for missions /// public static readonly CVarDef - SalvageExpeditionCooldown = CVarDef.Create("salvage.expedition_cooldown", 300f, CVar.REPLICATED); + SalvageExpeditionDuration = CVarDef.Create("salvage.expedition_duration", 420f, CVar.REPLICATED); + /// + /// Cooldown for missions. + /// public static readonly CVarDef - SalvageExpeditionFailedCooldown = CVarDef.Create("salvage.expedition_failed_cooldown", 900f, CVar.REPLICATED); + SalvageExpeditionCooldown = CVarDef.Create("salvage.expedition_cooldown", 780f, CVar.REPLICATED); /* * Flavor diff --git a/Content.Shared/Procedural/Loot/RandomSpawnsLoot.cs b/Content.Shared/Procedural/Loot/RandomSpawnsLoot.cs new file mode 100644 index 0000000000..a9b71e7b61 --- /dev/null +++ b/Content.Shared/Procedural/Loot/RandomSpawnsLoot.cs @@ -0,0 +1,33 @@ +using Content.Shared.Random; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Procedural.Loot; + +/// +/// Randomly places loot in free areas inside the dungeon. +/// +public sealed partial class RandomSpawnsLoot : IDungeonLoot +{ + [ViewVariables(VVAccess.ReadWrite), DataField("entries", required: true)] + public List Entries = new(); +} + +[DataDefinition] +public partial record struct RandomSpawnLootEntry : IBudgetEntry +{ + [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Proto { get; set; } = string.Empty; + + /// + /// Cost for this loot to spawn. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("cost")] + public float Cost { get; set; } = 1f; + + /// + /// Unit probability for this entry. Weighted against the entire table. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("prob")] + public float Prob { get; set; } = 1f; +} diff --git a/Content.Shared/Procedural/Loot/SalvageLootPrototype.cs b/Content.Shared/Procedural/Loot/SalvageLootPrototype.cs index f5f8ab3fb2..ba684dfe43 100644 --- a/Content.Shared/Procedural/Loot/SalvageLootPrototype.cs +++ b/Content.Shared/Procedural/Loot/SalvageLootPrototype.cs @@ -1,6 +1,4 @@ -using Content.Shared.Salvage; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Procedural.Loot; @@ -17,14 +15,6 @@ public sealed class SalvageLootPrototype : IPrototype /// [DataField("guaranteed")] public bool Guaranteed; - [DataField("desc")] public string Description = string.Empty; - - /// - /// Mission types this loot is not allowed to spawn for - /// - [DataField("blacklist")] - public List Blacklist = new(); - /// /// All of the loot rules /// diff --git a/Content.Shared/Procedural/SalvageDifficultyPrototype.cs b/Content.Shared/Procedural/SalvageDifficultyPrototype.cs new file mode 100644 index 0000000000..335bebde3f --- /dev/null +++ b/Content.Shared/Procedural/SalvageDifficultyPrototype.cs @@ -0,0 +1,36 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Procedural; + +[Prototype("salvageDifficulty")] +public sealed class SalvageDifficultyPrototype : IPrototype +{ + [IdDataField] public string ID { get; } = string.Empty; + + /// + /// Color to be used in UI. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("color")] + public Color Color = Color.White; + + /// + /// How much loot this difficulty is allowed to spawn. + /// + [DataField("lootBudget", required : true)] + public float LootBudget; + + /// + /// How many mobs this difficulty is allowed to spawn. + /// + [DataField("mobBudget", required : true)] + public float MobBudget; + + /// + /// Budget allowed for mission modifiers like no light, etc. + /// + [DataField("modifierBudget")] + public float ModifierBudget; + + [DataField("recommendedPlayers", required: true)] + public int RecommendedPlayers; +} diff --git a/Content.Shared/Random/IBudgetEntry.cs b/Content.Shared/Random/IBudgetEntry.cs new file mode 100644 index 0000000000..22dc69ed00 --- /dev/null +++ b/Content.Shared/Random/IBudgetEntry.cs @@ -0,0 +1,20 @@ +namespace Content.Shared.Random; + +/// +/// Budgeted random spawn entry. +/// +public interface IBudgetEntry : IProbEntry +{ + float Cost { get; set; } + + string Proto { get; set; } +} + +/// +/// Random entry that has a prob. See +/// +public interface IProbEntry +{ + float Prob { get; set; } +} + diff --git a/Content.Shared/Random/RandomSystem.cs b/Content.Shared/Random/RandomSystem.cs new file mode 100644 index 0000000000..78297e1da5 --- /dev/null +++ b/Content.Shared/Random/RandomSystem.cs @@ -0,0 +1,58 @@ +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Shared.Random; + +public sealed class RandomSystem : EntitySystem +{ + public IBudgetEntry? GetBudgetEntry(ref float budget, ref float probSum, IList entries, System.Random random) + { + DebugTools.Assert(budget > 0f); + + if (entries.Count == 0) + return null; + + // - Pick an entry + // - Remove the cost from budget + // - If our remaining budget is under maxCost then start pruning unavailable entries. + random.Shuffle(entries); + var budgetEntry = (IBudgetEntry) GetProbEntry(entries, probSum, random); + + budget -= budgetEntry.Cost; + + // Prune invalid entries. + for (var i = 0; i < entries.Count; i++) + { + var entry = entries[i]; + + if (entry.Cost < budget) + continue; + + entries.RemoveSwap(i); + i--; + probSum -= entry.Prob; + } + + return budgetEntry; + } + + /// + /// Gets a random entry based on each entry having a different probability. + /// + public IProbEntry GetProbEntry(IEnumerable entries, float probSum, System.Random random) + { + var value = random.NextFloat() * probSum; + + foreach (var entry in entries) + { + value -= entry.Prob; + + if (value < 0f) + { + return entry; + } + } + + throw new InvalidOperationException(); + } +} diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageAirMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageAirMod.cs index aee79fdf02..717c21947b 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageAirMod.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageAirMod.cs @@ -24,7 +24,7 @@ public sealed class SalvageAirMod : IPrototype, IBiomeSpecificMod public float Cost { get; private set; } = 0f; /// - [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List? Biomes { get; private set; } = null; /// diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs similarity index 92% rename from Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeMod.cs rename to Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs index fc6ce7e9de..fe806f2cd3 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeMod.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Salvage.Expeditions.Modifiers; /// Affects the biome to be used for salvage. /// [Prototype("salvageBiomeMod")] -public sealed class SalvageBiomeMod : IPrototype, ISalvageMod +public sealed class SalvageBiomeModPrototype : IPrototype, ISalvageMod { [IdDataField] public string ID { get; } = default!; diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageDungeonMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageDungeonModPrototype.cs similarity index 88% rename from Content.Shared/Salvage/Expeditions/Modifiers/SalvageDungeonMod.cs rename to Content.Shared/Salvage/Expeditions/Modifiers/SalvageDungeonModPrototype.cs index 335a127081..f86f7cfd3b 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageDungeonMod.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageDungeonModPrototype.cs @@ -6,7 +6,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Shared.Salvage.Expeditions.Modifiers; [Prototype("salvageDungeonMod")] -public sealed class SalvageDungeonMod : IPrototype, IBiomeSpecificMod +public sealed class SalvageDungeonModPrototype : IPrototype, IBiomeSpecificMod { [IdDataField] public string ID { get; } = default!; @@ -17,7 +17,7 @@ public sealed class SalvageDungeonMod : IPrototype, IBiomeSpecificMod public float Cost { get; private set; } = 0f; /// - [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List? Biomes { get; private set; } = null; /// diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageLightMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageLightMod.cs index e738c98a3a..cfdc6a2b76 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageLightMod.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageLightMod.cs @@ -15,7 +15,7 @@ public sealed class SalvageLightMod : IPrototype, IBiomeSpecificMod public float Cost { get; private set; } = 0f; /// - [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List? Biomes { get; private set; } = null; [DataField("color", required: true)] public Color? Color; diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTemperatureMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTemperatureMod.cs index c52b2010e4..bc3d5eb851 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTemperatureMod.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTemperatureMod.cs @@ -16,7 +16,7 @@ public sealed class SalvageTemperatureMod : IPrototype, IBiomeSpecificMod public float Cost { get; private set; } = 0f; /// - [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List? Biomes { get; private set; } = null; /// diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTimeMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTimeMod.cs deleted file mode 100644 index 6cc3507538..0000000000 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageTimeMod.cs +++ /dev/null @@ -1,23 +0,0 @@ -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; private set; } = string.Empty; - - /// - /// Cost for difficulty modifiers. - /// - [DataField("cost")] - public float Cost { get; private set; } - - [DataField("minDuration")] - public int MinDuration = 630; - - [DataField("maxDuration")] - public int MaxDuration = 570; -} diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageWeatherMod.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageWeatherMod.cs index caa89afeb4..89fc84c416 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageWeatherMod.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageWeatherMod.cs @@ -17,7 +17,7 @@ public sealed class SalvageWeatherMod : IPrototype, IBiomeSpecificMod public float Cost { get; private set; } = 0f; /// - [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField("biomes", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List? Biomes { get; private set; } = null; /// diff --git a/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs b/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs index 075149c1cb..41f44f672b 100644 --- a/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs +++ b/Content.Shared/Salvage/Expeditions/SalvageExpeditions.cs @@ -72,28 +72,14 @@ public sealed partial class SalvageExpeditionDataComponent : Component } [Serializable, NetSerializable] -public sealed record SalvageMissionParams : IComparable +public sealed record SalvageMissionParams { [ViewVariables] public ushort Index; - [ViewVariables(VVAccess.ReadWrite)] - public SalvageMissionType MissionType; - [ViewVariables(VVAccess.ReadWrite)] public int Seed; - /// - /// Base difficulty for this mission. - /// - [ViewVariables(VVAccess.ReadWrite)] public DifficultyRating Difficulty; - - public int CompareTo(SalvageMissionParams? other) - { - if (other == null) - return -1; - - return Difficulty.CompareTo(other.Difficulty); - } + public string Difficulty = string.Empty; } /// @@ -102,16 +88,13 @@ public sealed record SalvageMissionParams : IComparable /// public sealed record SalvageMission( int Seed, - DifficultyRating Difficulty, string Dungeon, string Faction, - SalvageMissionType Mission, string Biome, string Air, float Temperature, Color? Color, TimeSpan Duration, - List Rewards, List Modifiers) { /// @@ -120,12 +103,7 @@ public sealed record SalvageMission( public readonly int Seed = Seed; /// - /// Difficulty rating. - /// - public DifficultyRating Difficulty = Difficulty; - - /// - /// to be used. + /// to be used. /// public readonly string Dungeon = Dungeon; @@ -134,11 +112,6 @@ public sealed record SalvageMission( /// public readonly string Faction = Faction; - /// - /// Underlying mission params that generated this. - /// - public readonly SalvageMissionType Mission = Mission; - /// /// Biome to be used for the mission. /// @@ -164,11 +137,6 @@ public sealed record SalvageMission( /// public TimeSpan Duration = Duration; - /// - /// The list of items to order on mission completion. - /// - public List Rewards = Rewards; - /// /// Modifiers (outside of the above) applied to the mission. /// diff --git a/Content.Shared/Salvage/Expeditions/SalvageFactionPrototype.cs b/Content.Shared/Salvage/Expeditions/SalvageFactionPrototype.cs index cead062431..9de6d5221b 100644 --- a/Content.Shared/Salvage/Expeditions/SalvageFactionPrototype.cs +++ b/Content.Shared/Salvage/Expeditions/SalvageFactionPrototype.cs @@ -5,20 +5,14 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Shared.Salvage.Expeditions; [Prototype("salvageFaction")] -public sealed class SalvageFactionPrototype : IPrototype, ISalvageMod +public sealed class SalvageFactionPrototype : IPrototype { [IdDataField] public string ID { get; } = default!; [DataField("desc")] public string Description { get; private set; } = string.Empty; - /// - /// Cost for difficulty modifiers. - /// - [DataField("cost")] - public float Cost { get; private set; } = 0f; - - [ViewVariables(VVAccess.ReadWrite), DataField("groups", required: true)] - public List MobGroups = default!; + [ViewVariables(VVAccess.ReadWrite), DataField("entries", required: true)] + public List MobGroups = new(); /// /// Miscellaneous data for factions. diff --git a/Content.Shared/Salvage/Expeditions/SalvageMobEntry.cs b/Content.Shared/Salvage/Expeditions/SalvageMobEntry.cs new file mode 100644 index 0000000000..fcfd865d29 --- /dev/null +++ b/Content.Shared/Salvage/Expeditions/SalvageMobEntry.cs @@ -0,0 +1,24 @@ +using Content.Shared.Random; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Salvage.Expeditions; + +[DataDefinition] +public partial record struct SalvageMobEntry() : IBudgetEntry +{ + /// + /// Cost for this mob in a budget. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("cost")] + public float Cost { get; set; } = 1f; + + /// + /// Probability to spawn this mob. Summed with everything else for the faction. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("prob")] + public float Prob { get; set; } = 1f; + + [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string Proto { get; set; } = string.Empty; +} diff --git a/Content.Shared/Salvage/Expeditions/SalvageMobGroup.cs b/Content.Shared/Salvage/Expeditions/SalvageMobGroup.cs deleted file mode 100644 index df1917acdc..0000000000 --- a/Content.Shared/Salvage/Expeditions/SalvageMobGroup.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Shared.Storage; - -namespace Content.Shared.Salvage.Expeditions; - -[DataDefinition] -public partial record struct SalvageMobGroup() -{ - // A mob may be cheap but rare or expensive but frequent. - - /// - /// Probability to spawn this group. Summed with everything else for the faction. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("prob")] - public float Prob = 1f; - - [ViewVariables(VVAccess.ReadWrite), DataField("entries", required: true)] - public List Entries = new(); -} diff --git a/Content.Shared/Salvage/SharedSalvageSystem.cs b/Content.Shared/Salvage/SharedSalvageSystem.cs index 5c1bf20f21..918b18f8df 100644 --- a/Content.Shared/Salvage/SharedSalvageSystem.cs +++ b/Content.Shared/Salvage/SharedSalvageSystem.cs @@ -1,9 +1,13 @@ using System.Linq; +using Content.Shared.CCVar; using Content.Shared.Dataset; +using Content.Shared.Procedural; +using Content.Shared.Procedural.Loot; using Content.Shared.Random; using Content.Shared.Random.Helpers; using Content.Shared.Salvage.Expeditions; using Content.Shared.Salvage.Expeditions.Modifiers; +using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; @@ -13,73 +17,14 @@ namespace Content.Shared.Salvage; public abstract class SharedSalvageSystem : EntitySystem { - [Dependency] private readonly ILocalizationManager _loc = default!; + [Dependency] protected readonly IConfigurationManager CfgManager = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - #region Descriptions - - public string GetMissionDescription(SalvageMission mission) - { - // 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(mission.Faction).Configs["DefenseStructure"]; - - return Loc.GetString("salvage-expedition-desc-structure", - ("count", GetStructureCount(mission.Difficulty)), - ("structure", _loc.GetEntityData(proto).Name)); - case SalvageMissionType.Elimination: - return Loc.GetString("salvage-expedition-desc-elimination"); - default: - throw new NotImplementedException(); - } - } - - public float GetMiningTax(DifficultyRating baseRating) - { - return 0.6f + (int) baseRating * 0.05f; - } - /// - /// Gets the amount of structures to destroy. + /// Main loot table for salvage expeditions. /// - public int GetStructureCount(DifficultyRating baseRating) - { - return 1 + (int) baseRating * 2; - } - - #endregion - - public int GetDifficulty(DifficultyRating rating) - { - switch (rating) - { - case DifficultyRating.Minimal: - return 1; - case DifficultyRating.Minor: - return 2; - case DifficultyRating.Moderate: - return 4; - case DifficultyRating.Hazardous: - return 8; - case DifficultyRating.Extreme: - return 16; - default: - throw new ArgumentOutOfRangeException(nameof(rating), rating, null); - } - } - - /// - /// How many groups of mobs to spawn for a mission. - /// - public float GetSpawnCount(DifficultyRating difficulty) - { - return (int) difficulty * 2; - } + [ValidatePrototypeId] + public const string ExpeditionsLootProto = "SalvageLoot"; public static string GetFTLName(DatasetPrototype dataset, int seed) { @@ -87,51 +32,45 @@ public abstract class SharedSalvageSystem : EntitySystem return $"{dataset.Values[random.Next(dataset.Values.Count)]}-{random.Next(10, 100)}-{(char) (65 + random.Next(26))}"; } - public SalvageMission GetMission(SalvageMissionType config, DifficultyRating difficulty, int seed) + public SalvageMission GetMission(SalvageDifficultyPrototype difficulty, int seed) { // 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 modifierBudget = difficulty.ModifierBudget; var rand = new System.Random(seed); - var faction = GetMod(rand, ref rating); - var biome = GetMod(rand, ref rating); - var dungeon = GetBiomeMod(biome.ID, rand, ref rating); + + // Run budget in order of priority + // - Biome + // - Lighting + // - Atmos + var biome = GetMod(rand, ref modifierBudget); + var light = GetBiomeMod(biome.ID, rand, ref modifierBudget); + var temp = GetBiomeMod(biome.ID, rand, ref modifierBudget); + var air = GetBiomeMod(biome.ID, rand, ref modifierBudget); + var dungeon = GetBiomeMod(biome.ID, rand, ref modifierBudget); + var factionProtos = _proto.EnumeratePrototypes().ToList(); + var faction = factionProtos[rand.Next(factionProtos.Count)]; + var mods = new List(); - var air = GetBiomeMod(biome.ID, rand, ref rating); if (air.Description != string.Empty) { mods.Add(air.Description); } // only show the description if there is an atmosphere since wont matter otherwise - var temp = GetBiomeMod(biome.ID, rand, ref rating); if (temp.Description != string.Empty && !air.Space) { mods.Add(temp.Description); } - var light = GetBiomeMod(biome.ID, rand, ref rating); if (light.Description != string.Empty) { mods.Add(light.Description); } - var time = GetMod(rand, ref rating); - // Round the duration to nearest 15 seconds. - var exactDuration = MathHelper.Lerp(time.MinDuration, time.MaxDuration, rand.NextFloat()); - exactDuration = MathF.Round(exactDuration / 15f) * 15f; - var duration = TimeSpan.FromSeconds(exactDuration); + var duration = TimeSpan.FromSeconds(CfgManager.GetCVar(CCVars.SalvageExpeditionDuration)); - if (time.Description != string.Empty) - { - mods.Add(time.Description); - } - - var rewards = GetRewards(difficulty, rand); - return new SalvageMission(seed, difficulty, dungeon.ID, faction.ID, config, biome.ID, air.ID, temp.Temperature, light.Color, duration, rewards, mods); + return new SalvageMission(seed, dungeon.ID, faction.ID, biome.ID, air.ID, temp.Temperature, light.Color, duration, mods); } public T GetBiomeMod(string biome, System.Random rand, ref float rating) where T : class, IPrototype, IBiomeSpecificMod @@ -171,72 +110,5 @@ public abstract class SharedSalvageSystem : EntitySystem throw new InvalidOperationException(); } - - private List GetRewards(DifficultyRating difficulty, System.Random rand) - { - var rewards = new List(3); - var ids = RewardsForDifficulty(difficulty); - foreach (var id in ids) - { - // pick a random reward to give - var weights = _proto.Index(id); - rewards.Add(weights.Pick(rand)); - } - - return rewards; - } - - /// - /// Get a list of WeightedRandomEntityPrototype IDs with the rewards for a certain difficulty. - /// - private string[] RewardsForDifficulty(DifficultyRating rating) - { - var common = "SalvageRewardCommon"; - var rare = "SalvageRewardRare"; - var epic = "SalvageRewardEpic"; - switch (rating) - { - case DifficultyRating.Minimal: - return new string[] { common, common, common }; - case DifficultyRating.Minor: - return new string[] { common, common, rare }; - case DifficultyRating.Moderate: - return new string[] { common, rare, rare }; - case DifficultyRating.Hazardous: - return new string[] { rare, rare, rare, epic }; - case DifficultyRating.Extreme: - return new string[] { rare, rare, epic, epic, epic }; - default: - throw new NotImplementedException(); - } - } } -[Serializable, NetSerializable] -public enum SalvageMissionType : byte -{ - /// - /// No dungeon, just ore loot and random mob spawns. - /// - Mining, - - /// - /// Destroy the specified structures in a dungeon. - /// - Destruction, - - /// - /// Kill a large creature in a dungeon. - /// - Elimination, -} - -[Serializable, NetSerializable] -public enum DifficultyRating : byte -{ - Minimal, - Minor, - Moderate, - Hazardous, - Extreme, -} diff --git a/Resources/ConfigPresets/Build/development.toml b/Resources/ConfigPresets/Build/development.toml index fd3c075f7d..82833fe2d5 100644 --- a/Resources/ConfigPresets/Build/development.toml +++ b/Resources/ConfigPresets/Build/development.toml @@ -14,7 +14,6 @@ preload = false [salvage] expedition_cooldown = 30.0 -expedition_failed_cooldown = 30.0 [shuttle] grid_fill = false diff --git a/Resources/Locale/en-US/procedural/expeditions.ftl b/Resources/Locale/en-US/procedural/expeditions.ftl index a0192fabd3..f309da1bda 100644 --- a/Resources/Locale/en-US/procedural/expeditions.ftl +++ b/Resources/Locale/en-US/procedural/expeditions.ftl @@ -4,8 +4,7 @@ salvage-expedition-structure-remaining = {$count -> *[other] {$count} structures remaining. } -salvage-expedition-megafauna-remaining = {$count} megafauna remaining. - +salvage-expedition-type = Mission salvage-expedition-window-title = Salvage expeditions salvage-expedition-window-difficulty = Difficulty: salvage-expedition-window-details = Details: @@ -13,31 +12,17 @@ salvage-expedition-window-hostiles = Hostiles: salvage-expedition-window-duration = Duration: salvage-expedition-window-biome = Biome: salvage-expedition-window-modifiers = Modifiers: -salvage-expedition-window-rewards = Rewards: salvage-expedition-window-claimed = Claimed salvage-expedition-window-claim = Claim salvage-expedition-window-next = Next offer -# Expedition descriptions -salvage-expedition-desc-mining = Collect resources inside the area. -# You will be taxed {$tax}% of the resources collected. -salvage-expedition-desc-structure = {$count -> - [one] Destroy {$count} {$structure} inside the area. - *[other] Destroy {$count} {$structure}s inside the area. -} -salvage-expedition-desc-elimination = Kill a large and dangerous creature inside the area. - -salvage-expedition-type-Mining = Mining -salvage-expedition-type-Destruction = Destruction -salvage-expedition-type-Elimination = Elimination - -salvage-expedition-difficulty-Minimal = Minimal -salvage-expedition-difficulty-Minor = Minor salvage-expedition-difficulty-Moderate = Moderate salvage-expedition-difficulty-Hazardous = Hazardous salvage-expedition-difficulty-Extreme = Extreme +salvage-expedition-difficulty-players = Recommended salvagers: + # Runner salvage-expedition-not-all-present = Not all salvagers are aboard the shuttle! diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index 2d490ddb1c..5b32601033 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -164,7 +164,14 @@ parent: MobCarp suffix: Dungeon components: + - type: MobThresholds + thresholds: + 0: Alive + 50: Dead + - type: SlowOnDamage + speedModifierThresholds: + 25: 0.7 - type: MeleeWeapon damage: types: - Slash: 5 + Slash: 6 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 3eaee32998..1c81d57e69 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -60,6 +60,9 @@ thresholds: 0: Alive 50: Dead + - type: SlowOnDamage + speedModifierThresholds: + 25: 0.5 - type: Stamina critThreshold: 200 - type: Bloodstream @@ -129,7 +132,7 @@ - type: MobThresholds thresholds: 0: Alive - 75: Dead + 100: Dead - type: Stamina critThreshold: 300 - type: SlowOnDamage @@ -162,18 +165,16 @@ - type: MobThresholds thresholds: 0: Alive - 150: Dead + 80: Dead + - type: SlowOnDamage + speedModifierThresholds: + 40: 0.7 - type: MeleeWeapon damage: groups: - Brute: 5 + Brute: 6 - type: MovementSpeedModifier - baseWalkSpeed: 2.0 - baseSprintSpeed: 2.5 - - type: SlowOnDamage - speedModifierThresholds: - 100: 0.4 - 50: 0.7 + baseSprintSpeed: 4 - type: Fixtures fixtures: fix1: @@ -202,20 +203,15 @@ thresholds: 0: Alive 300: Dead - - type: Stamina - critThreshold: 1500 + - type: SlowOnDamage + speedModifierThresholds: + 150: 0.7 - type: MovementSpeedModifier - baseWalkSpeed: 2.8 - baseSprintSpeed: 3.8 - type: MeleeWeapon hidden: true damage: groups: - Brute: 20 - - type: SlowOnDamage - speedModifierThresholds: - 250: 0.4 - 200: 0.7 + Brute: 12 - type: Fixtures fixtures: fix1: @@ -246,12 +242,9 @@ - type: MobThresholds thresholds: 0: Alive - 200: Dead - - type: Stamina - critThreshold: 550 + 100: Dead - type: MovementSpeedModifier - baseWalkSpeed: 2.3 - baseSprintSpeed: 4.2 + baseSprintSpeed: 4 - type: MeleeWeapon hidden: true damage: @@ -259,8 +252,7 @@ Brute: 10 - type: SlowOnDamage speedModifierThresholds: - 150: 0.5 - 100: 0.7 + 50: 0.7 - type: Fixtures fixtures: fix1: @@ -285,16 +277,13 @@ layers: - map: ["enum.DamageStateVisualLayers.Base"] state: running - - type: Stamina - critThreshold: 250 - type: MovementSpeedModifier - baseWalkSpeed: 2.7 baseSprintSpeed: 6.0 - type: MeleeWeapon hidden: true damage: groups: - Brute: 3 + Brute: 5 - type: Fixtures fixtures: fix1: @@ -332,12 +321,13 @@ - type: MobThresholds thresholds: 0: Alive - 75: Dead + 50: Dead + - type: SlowOnDamage + speedModifierThresholds: + 25: 0.7 - type: HTN rootTask: task: SimpleRangedHostileCompound - - type: Stamina - critThreshold: 300 - type: RechargeBasicEntityAmmo rechargeCooldown: 0.75 - type: BasicEntityAmmoProvider @@ -351,9 +341,6 @@ availableModes: - FullAuto soundGunshot: /Audio/Weapons/Xeno/alien_spitacid.ogg - - type: SlowOnDamage - speedModifierThresholds: - 50: 0.4 - type: Fixtures fixtures: fix1: diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 7360076772..398d101021 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -144,21 +144,22 @@ components: - type: GhostRole description: ghost-role-information-space-dragon-dungeon-description - # less tanky, no crit + - type: SlowOnDamage + speedModifierThresholds: + 100: 0.7 - type: MobThresholds thresholds: 0: Alive - 300: Dead + 200: Dead # less meat spawned since it's a lot easier to kill - type: Butcherable spawned: - id: FoodMeatDragon amount: 1 - # half damage, spread evenly - type: MeleeWeapon damage: groups: - Brute: 15 + Brute: 12 - type: entity id: ActionSpawnRift diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml index f51c3ada6a..513e691395 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml @@ -13,16 +13,16 @@ - type: ItemCooldown - type: MeleeWeapon damage: + groups: + Brute: 5 types: - Blunt: 2.5 - Piercing: 2.5 Structural: 10 - type: Wieldable - type: IncreaseDamageOnWield damage: + groups: + Brute: 10 types: - Blunt: 5 - Piercing: 5 Structural: 10 - type: Item size: 80 @@ -44,7 +44,7 @@ - type: MeleeWeapon attackRate: 1.5 damage: + groups: + Brute: 10 types: - Blunt: 5 - Piercing: 5 Structural: 10 diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml index d3560b8b68..73b7887539 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml @@ -44,6 +44,7 @@ - type: entity id: Thruster + name: thruster parent: [ BaseThruster, ConstructibleMachine ] components: - type: Thruster @@ -64,6 +65,15 @@ visible: false offset: 0, 1 +- type: entity + id: ThrusterUnanchored + parent: Thruster + components: + - type: Transform + anchored: false + - type: Physics + bodyType: Dynamic + - type: entity id: DebugThruster parent: BaseThruster @@ -91,6 +101,7 @@ - type: entity id: Gyroscope + name: gyroscope parent: [ BaseThruster, ConstructibleMachine ] components: - type: Thruster @@ -133,6 +144,15 @@ - type: StaticPrice price: 2000 +- type: entity + id: GyroscopeUnanchored + parent: Gyroscope + components: + - type: Transform + anchored: false + - type: Physics + bodyType: Dynamic + - type: entity id: DebugGyroscope parent: BaseThruster diff --git a/Resources/Prototypes/Procedural/salvage_difficulties.yml b/Resources/Prototypes/Procedural/salvage_difficulties.yml new file mode 100644 index 0000000000..caef822329 --- /dev/null +++ b/Resources/Prototypes/Procedural/salvage_difficulties.yml @@ -0,0 +1,12 @@ +- type: salvageDifficulty + id: Moderate + lootBudget: 30 + mobBudget: 25 + modifierBudget: 2 + color: "#52B4E996" + recommendedPlayers: 2 + + #9FED5896 + #EFB34196 + #DE3A3A96 + #D381C996 diff --git a/Resources/Prototypes/Procedural/salvage_factions.yml b/Resources/Prototypes/Procedural/salvage_factions.yml index f0aba69908..1097ed9049 100644 --- a/Resources/Prototypes/Procedural/salvage_factions.yml +++ b/Resources/Prototypes/Procedural/salvage_factions.yml @@ -1,61 +1,41 @@ - type: salvageFaction id: Xenos - groups: - - entries: - - id: MobXeno - amount: 2 - maxAmount: 3 - - id: MobXenoDrone - amount: 1 - - entries: - - id: MobXenoPraetorian - amount: 1 - maxAmount: 2 - prob: 0.5 - - entries: - - id: MobXenoDrone - amount: 0 - maxAmount: 2 - prob: 0.25 - - entries: - - id: WeaponTurretXeno - amount: 3 - prob: 0.25 - - entries: - - id: MobXenoSpitter - amount: 2 - prob: 0.25 - - entries: - - id: MobXenoRavager - amount: 1 - prob: 0.1 - - entries: - - id: MobXenoRouny - amount: 1 - prob: 0.001 + entries: + - proto: MobXeno + - proto: MobXenoDrone + cost: 2 + - proto: MobXenoPraetorian + cost: 5 + prob: 0.1 + - proto: MobXenoQueen + cost: 10 + prob: 0.02 + - proto: MobXenoRavager + cost: 5 + - proto: MobXenoRouny + cost: 3 + prob: 0.02 + - proto: MobXenoSpitter + - proto: WeaponTurretXeno + prob: 0.1 configs: DefenseStructure: XenoWardingTower - Mining: Xenos Megafauna: MobXenoQueen - type: salvageFaction id: Carps - groups: - - entries: - - id: MobCarpDungeon - amount: 1 - maxAmount: 4 - - entries: - - id: MobCarpMagic - amount: 1 - maxAmount: 3 - prob: 0.5 - - entries: - - id: MobCarpHolo - amount: 1 - maxAmount: 2 - prob: 0.25 + entries: + - proto: MobCarpDungeon + # These do too much damage for salvage, need nerfs + #- proto: MobCarpHolo + # cost: 5 + # prob: 0.1 + #- proto: MobCarpMagic + # cost: 5 + # prob: 0.1 + - proto: MobDragonDungeon + cost: 10 + prob: 0.02 configs: DefenseStructure: CarpStatue - Mining: Carps Megafauna: MobDragonDungeon diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index 06da9f7167..54870943b2 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -1,8 +1,125 @@ -# Ores +# Loot table +# Main loot table for random spawns +- type: salvageLoot + id: SalvageLoot + loots: + - !type:RandomSpawnsLoot + entries: + - proto: AdvMopItem + prob: 0.5 + - proto: AmmoTechFabCircuitboard + cost: 2 + - proto: AutolatheMachineCircuitboard + cost: 2 + - proto: BiomassReclaimerMachineCircuitboard + cost: 2 + - proto: BluespaceBeaker + cost: 2 + - proto: CyborgEndoskeleton + cost: 3 + prob: 0.5 + - proto: ChemDispenserMachineCircuitboard + cost: 2 + - proto: CircuitImprinter + cost: 2 + - proto: CloningConsoleComputerCircuitboard + cost: 2 + - proto: CloningPodMachineCircuitboard + cost: 2 + - proto: CognizineChemistryBottle + - proto: CratePartsT3 + cost: 2 + prob: 0.5 + - proto: CratePartsT3T4 + cost: 5 + prob: 0.5 + - proto: CratePartsT4 + cost: 5 + prob: 0.5 + - proto: CrateSalvageEquipment + cost: 3 + prob: 0.5 + - proto: GasRecycler + cost: 2 + - proto: GeneratorRTG + cost: 5 + - proto: GravityGeneratorMini + cost: 2 + - proto: GyroscopeUnanchored + cost: 2 + prob: 0.1 + - proto: MedicalScannerMachineCircuitboard + cost: 2 + - proto: NuclearBombKeg + cost: 5 + - proto: OmnizineChemistryBottle + prob: 0.5 + - proto: PortableGeneratorPacman + cost: 2 + - proto: PortableGeneratorSuperPacman + cost: 3 + - proto: PowerCellAntiqueProto + cost: 5 + prob: 0.5 + - proto: ProtolatheMachineCircuitboard + - proto: RandomArtifactSpawner + cost: 2 + - proto: RandomCargoCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomCommandCorpseSpawner + cost: 5 + prob: 0.5 + - proto: RandomEngineerCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomMedicCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomScienceCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomSecurityCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomServiceCorpseSpawner + cost: 2 + prob: 0.5 + - proto: ResearchAndDevelopmentServerMachineCircuitboard + cost: 5 + prob: 0.5 + - proto: ResearchDisk10000 + prob: 0.5 + - proto: ResearchDisk5000 + prob: 0.5 + - proto: RipleyHarness + cost: 3 + prob: 0.5 + - proto: RPED + - proto: SpaceCash1000 + - proto: SpaceCash10000 + cost: 10 + - proto: SpaceCash2500 + cost: 3 + - proto: SpaceCash5000 + cost: 5 + - proto: TechnologyDiskRare + cost: 5 + prob: 0.5 + - proto: ThrusterUnanchored + - proto: WaterTankHighCapacity + - proto: WeldingFuelTankHighCapacity + cost: 3 + +# Mob loot table + + +# Boss loot table + +# Ores - these are guaranteed # - Low value - type: salvageLoot id: OreTin - desc: Veins of steel guaranteed: true loots: - !type:BiomeMarkerLoot @@ -14,7 +131,6 @@ - type: salvageLoot id: OreQuartz - desc: Veins of quartz guaranteed: true loots: - !type:BiomeMarkerLoot @@ -27,7 +143,6 @@ # - Medium value - type: salvageLoot id: OreGold - desc: Veins of gold ore guaranteed: true loots: - !type:BiomeMarkerLoot @@ -39,7 +154,6 @@ - type: salvageLoot id: OreSilver - desc: Veins of silver ore guaranteed: true loots: - !type:BiomeMarkerLoot @@ -52,7 +166,6 @@ # - High value - type: salvageLoot id: OrePlasma - desc: Veins of plasma ore guaranteed: true loots: - !type:BiomeMarkerLoot @@ -64,7 +177,6 @@ - type: salvageLoot id: OreUranium - desc: Veins of uranium ore guaranteed: true loots: - !type:BiomeMarkerLoot @@ -76,7 +188,6 @@ - type: salvageLoot id: OreBananium - desc: Veins of bananium ore guaranteed: true loots: - !type:BiomeMarkerLoot @@ -88,7 +199,6 @@ - type: salvageLoot id: OreArtifactFragment - desc: artifact fragment-embedded rock guaranteed: true loots: - !type:BiomeMarkerLoot diff --git a/Resources/Prototypes/Procedural/salvage_mods.yml b/Resources/Prototypes/Procedural/salvage_mods.yml index 7ff2918c13..8d46dff59a 100644 --- a/Resources/Prototypes/Procedural/salvage_mods.yml +++ b/Resources/Prototypes/Procedural/salvage_mods.yml @@ -4,58 +4,44 @@ parent: FTLPoint # Biome mods -> at least 1 required +- type: salvageBiomeMod + id: Caves + biome: Caves + - type: salvageBiomeMod id: Grasslands biome: Grasslands +- type: salvageBiomeMod + id: Snow + cost: 1 + biome: Snow + - type: salvageBiomeMod id: Lava cost: 2 biome: Lava -- type: salvageBiomeMod - id: Snow - biome: Snow - -- type: salvageBiomeMod - id: Caves - cost: 1 - biome: Caves - #- type: salvageBiomeMod # id: Space # cost: 1 # weather: false # biome: null -# Temperature mods -> not required -# Also whitelist it - -# Weather mods -> not required -- type: salvageWeatherMod - id: SnowfallHeavy - weather: SnowfallHeavy - cost: 1 - -- type: salvageWeatherMod - id: Rain - weather: Rain - # Light mods -> required -# At some stage with sub-biomes this will probably be moved onto the biome itself - type: salvageLightMod id: Daylight desc: Daylight color: "#D8B059" biomes: - - Grasslands + - Grasslands - type: salvageLightMod id: Lavalight desc: Daylight color: "#A34931" biomes: - - Lava + - Lava - type: salvageLightMod id: Evening @@ -65,24 +51,169 @@ - type: salvageLightMod id: Night desc: Night time - cost: 2 + cost: 1 color: null -# Time mods -> at least 1 required -- type: salvageTimeMod - id: StandardTime +# Temperatures +- type: salvageTemperatureMod + id: RoomTemp + cost: 0 -- type: salvageTimeMod - id: RushTime - desc: Rush - minDuration: 420 - maxDuration: 465 +- type: salvageTemperatureMod + id: Hot cost: 1 + temperature: 323.15 # 50C + biomes: + - Caves + #- LowDesert + - Grasslands + - Lava -# Misc mods -- type: salvageMod - id: LongDistance - desc: Long distance +- type: salvageTemperatureMod + id: Burning + desc: High temperature + cost: 2 + temperature: 423.15 # 200C + biomes: + - Caves + #- LowDesert + - Lava + +- type: salvageTemperatureMod + id: Melting + desc: Extreme heat + cost: 4 + temperature: 1273.15 # 1000C hot hot hot + biomes: + - Lava + +- type: salvageTemperatureMod + id: Cold + cost: 1 + temperature: 275.15 # 2C + biomes: + - Caves + #- LowDesert + - Grasslands + - Snow + +- type: salvageTemperatureMod + id: Tundra + desc: Low temperature + cost: 2 + temperature: 263.15 # -40C + biomes: + - Caves + - Snow + +- type: salvageTemperatureMod + id: Frozen + desc: Extreme cold + cost: 4 + temperature: 123.15 # -150C + biomes: + - Snow + +# Air mixtures +- type: salvageAirMod + id: Space + desc: No atmosphere + space: true + cost: 2 + biomes: + - Caves + - Lava + +- type: salvageAirMod + id: Breathable + cost: 0 + gases: + - 21.824779 # oxygen + - 82.10312 # nitrogen + +- type: salvageAirMod + id: Sleepy + cost: 1 + desc: Dangerous atmosphere + gases: + - 21.824779 # oxygen + - 72.10312 # nitrogen + - 0 + - 0 + - 0 + - 0 + - 0 + - 10 # nitrous oxide + biomes: + - Caves + #- LowDesert + - Snow + - Grasslands + - Lava + +- type: salvageAirMod + id: Poisoned + cost: 2 + desc: Dangerous atmosphere + gases: + - 21.824779 # oxygen + - 77.10312 # nitrogen + - 10 # carbon dioxide + biomes: + - Caves + #- LowDesert + - Snow + - Grasslands + - Lava + +- type: salvageAirMod + id: Poison + cost: 3 + desc: Toxic atmosphere + gases: + - 21.824779 # oxygen + - 0 + - 82.10312 # carbon dioxide + biomes: + - Caves + - Snow + - Lava + +- type: salvageAirMod + id: Plasma + cost: 4 + desc: Toxic atmosphere + gases: + - 0 + - 0 + - 0 + - 103.927899 # plasma + biomes: + - Caves + - Lava + +- type: salvageAirMod + id: Burnable + cost: 5 + desc: Volatile atmosphere + gases: + - 21.824779 # oxygen + - 0 + - 0 + - 82.10312 # plasma + biomes: + - Caves + - Lava + +# Weather mods -> not required +#- type: salvageWeatherMod +# id: SnowfallHeavy +# weather: SnowfallHeavy +# cost: 1 +# +#- type: salvageWeatherMod +# id: Rain +# weather: Rain # Dungeons # For now just simple 1-dungeon setups @@ -100,160 +231,3 @@ proto: LavaBrig biomes: - Lava - -# Air mixtures -- type: salvageAirMod - id: Space - desc: No atmosphere - space: true - cost: 1 - biomes: - - Caves - - Lava - -- type: salvageAirMod - id: Breathable - gases: - - 21.824779 # oxygen - - 82.10312 # nitrogen - biomes: - - Caves - #- LowDesert - - Snow - - Grasslands - -- type: salvageAirMod - id: Sleepy - cost: 1 - desc: Dangerous atmosphere - gases: - - 21.824779 # oxygen - - 72.10312 # nitrogen - - 0 - - 0 - - 0 - - 0 - - 0 - - 10 # nitrous oxide - biomes: - - Caves - #- LowDesert - - Snow - - Grasslands - - Lava - -- type: salvageAirMod - id: Poisoned - cost: 2 - desc: Dangerous atmosphere - gases: - - 21.824779 # oxygen - - 77.10312 # nitrogen - - 10 # carbon dioxide - biomes: - - Caves - #- LowDesert - - Snow - - Grasslands - - Lava - -- type: salvageAirMod - id: Poison - cost: 3 - desc: Toxic atmosphere - gases: - - 21.824779 # oxygen - - 0 - - 82.10312 # carbon dioxide - biomes: - - Caves - - Snow - - Lava - -- type: salvageAirMod - id: Plasma - cost: 4 - desc: Toxic atmosphere - gases: - - 0 - - 0 - - 0 - - 103.927899 # plasma - biomes: - - Caves - - Lava - -- type: salvageAirMod - id: Burnable - cost: 5 - desc: Volatile atmosphere - gases: - - 21.824779 # oxygen - - 0 - - 0 - - 82.10312 # plasma - biomes: - - Caves - - Lava - -# Temperatures - -- type: salvageTemperatureMod - id: RoomTemp - biomes: - - Caves - #- LowDesert - - Grasslands - -- type: salvageTemperatureMod - id: Hot - temperature: 323.15 # 50C - biomes: - - Caves - #- LowDesert - - Grasslands - - Lava - -- type: salvageTemperatureMod - id: Burning - desc: High temperature - cost: 1 - temperature: 423.15 # 200C - biomes: - - Caves - #- LowDesert - - Lava - -- type: salvageTemperatureMod - id: Melting - desc: Extreme heat - cost: 4 - temperature: 1273.15 # 1000C hot hot hot - biomes: - - Lava - -- type: salvageTemperatureMod - id: Cold - temperature: 275.15 # 2C - biomes: - - Caves - #- LowDesert - - Grasslands - - Snow - -- type: salvageTemperatureMod - id: Tundra - desc: Low temperature - cost: 2 - temperature: 263.15 # -40C - biomes: - - Caves - - Snow - -- type: salvageTemperatureMod - id: Frozen - desc: Extreme cold - cost: 3 - temperature: 123.15 # -150C - biomes: - - Snow