diff --git a/Content.Server/Research/Systems/ResearchStealerSystem.cs b/Content.Server/Research/Systems/ResearchStealerSystem.cs index 5bab6048de..d40134f1e9 100644 --- a/Content.Server/Research/Systems/ResearchStealerSystem.cs +++ b/Content.Server/Research/Systems/ResearchStealerSystem.cs @@ -1,11 +1,13 @@ using Content.Shared.Research.Components; using Content.Shared.Research.Systems; +using Robust.Shared.Random; namespace Content.Server.Research.Systems; public sealed class ResearchStealerSystem : SharedResearchStealerSystem { [Dependency] private readonly SharedResearchSystem _research = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -24,16 +26,26 @@ public sealed class ResearchStealerSystem : SharedResearchStealerSystem if (!TryComp(target, out var database)) return; - var ev = new ResearchStolenEvent(uid, target, database.UnlockedTechnologies); + var ev = new ResearchStolenEvent(uid, target, new()); + var count = _random.Next(comp.MinToSteal, comp.MaxToSteal + 1); + for (var i = 0; i < count; i++) + { + if (database.UnlockedTechnologies.Count == 0) + break; + + var toRemove = _random.Pick(database.UnlockedTechnologies); + if (_research.TryRemoveTechnology((target, database), toRemove)) + ev.Techs.Add(toRemove); + } RaiseLocalEvent(uid, ref ev); - // oops, no more advanced lasers! - _research.ClearTechs(target, database); + + args.Handled = true; } } /// -/// Event raised on the user when research is stolen from a R&D server. +/// Event raised on the user when research is stolen from a RND server. /// Techs contains every technology id researched. /// [ByRefEvent] -public record struct ResearchStolenEvent(EntityUid Used, EntityUid Target, List Techs); +public record struct ResearchStolenEvent(EntityUid Used, EntityUid Target, List Techs); diff --git a/Content.Shared/Research/Components/ResearchStealerComponent.cs b/Content.Shared/Research/Components/ResearchStealerComponent.cs index e0331fad1b..df8878e651 100644 --- a/Content.Shared/Research/Components/ResearchStealerComponent.cs +++ b/Content.Shared/Research/Components/ResearchStealerComponent.cs @@ -14,4 +14,16 @@ public sealed partial class ResearchStealerComponent : Component /// [DataField("delay"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan Delay = TimeSpan.FromSeconds(20); + + /// + /// The minimum number of technologies that will be stolen + /// + [DataField] + public int MinToSteal = 4; + + /// + /// The maximum number of technologies that will be stolen + /// + [DataField] + public int MaxToSteal = 8; } diff --git a/Content.Shared/Research/Systems/SharedResearchSystem.cs b/Content.Shared/Research/Systems/SharedResearchSystem.cs index 12f27d0b9c..9819e949b8 100644 --- a/Content.Shared/Research/Systems/SharedResearchSystem.cs +++ b/Content.Shared/Research/Systems/SharedResearchSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; +using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -219,16 +220,58 @@ public abstract class SharedResearchSystem : EntitySystem if (!Resolve(uid, ref component)) return; - var discipline = PrototypeManager.Index(prototype.Discipline); + var discipline = PrototypeManager.Index(prototype.Discipline); if (prototype.Tier < discipline.LockoutTier) return; component.MainDiscipline = prototype.Discipline; Dirty(uid, component); } + /// + /// Removes a technology and its recipes from a technology database. + /// + public bool TryRemoveTechnology(Entity entity, ProtoId tech) + { + return TryRemoveTechnology(entity, PrototypeManager.Index(tech)); + } + + /// + /// Removes a technology and its recipes from a technology database. + /// + [PublicAPI] + public bool TryRemoveTechnology(Entity entity, TechnologyPrototype tech) + { + if (!entity.Comp.UnlockedTechnologies.Remove(tech.ID)) + return false; + + // check to make sure we didn't somehow get the recipe from another tech. + // unlikely, but whatever + var recipes = tech.RecipeUnlocks; + foreach (var recipe in recipes) + { + var hasTechElsewhere = false; + foreach (var unlockedTech in entity.Comp.UnlockedTechnologies) + { + var unlockedTechProto = PrototypeManager.Index(unlockedTech); + + if (!unlockedTechProto.RecipeUnlocks.Contains(recipe)) + continue; + hasTechElsewhere = true; + break; + } + + if (!hasTechElsewhere) + entity.Comp.UnlockedRecipes.Remove(recipe); + } + Dirty(entity, entity.Comp); + UpdateTechnologyCards(entity, entity); + return true; + } + /// /// Clear all unlocked technologies from the database. /// + [PublicAPI] public void ClearTechs(EntityUid uid, TechnologyDatabaseComponent? comp = null) { if (!Resolve(uid, ref comp) || comp.UnlockedTechnologies.Count == 0) diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index 43def65d7a..f2ac97be58 100644 --- a/Resources/Prototypes/Objectives/ninja.yml +++ b/Resources/Prototypes/Objectives/ninja.yml @@ -39,8 +39,8 @@ sprite: Structures/Machines/server.rsi state: server - type: NumberObjective - min: 5 - max: 10 + min: 9 + max: 13 title: objective-condition-steal-research-title - type: StealResearchCondition