diff --git a/Content.Server/EntityEffects/Effects/MakeSyndient.cs b/Content.Server/EntityEffects/Effects/MakeSyndient.cs new file mode 100644 index 00000000000000..1f7745cb2314ea --- /dev/null +++ b/Content.Server/EntityEffects/Effects/MakeSyndient.cs @@ -0,0 +1,121 @@ +using Content.Server.Forensics; +using Content.Server.Chat.Managers; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Speech.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.EntityEffects; +using Content.Shared.Humanoid; +using Content.Shared.Mind.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Random; +using Robust.Shared.Prototypes; +using YamlDotNet.Core.Tokens; +using System.Linq; +using Content.Server.Atmos.EntitySystems; + +namespace Content.Server.EntityEffects.Effects; + +public sealed partial class MakeSyndient : EntityEffect +{ + //this is basically completely copied from MakeSentient, but with a bit of changes to how the ghost roles are listed + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + => Loc.GetString("reagent-effect-guidebook-make-sentient", ("chance", Probability)); + + [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; + + public override void Effect(EntityEffectBaseArgs args) + { + + var entityManager = args.EntityManager; + var uid = args.TargetEntity; + + + // Let affected entities speak normally to make this effect different from, say, the "random sentience" event + // This also works on entities that already have a mind + // We call this before the mind check to allow things like player-controlled mice to be able to benefit from the effect + entityManager.RemoveComponent(uid); + entityManager.RemoveComponent(uid); + + // Stops from adding a ghost role to things like people who already have a mind + if (entityManager.TryGetComponent(uid, out var mindContainer) && mindContainer.HasMind) + { + return; + } + + //slightly hacky way to make sure it doesn't work on humanoid ghost roles that haven't been claimed yet + if (entityManager.TryGetComponent(uid, out HumanoidAppearanceComponent? component)) + { + return; + } + var forensicSys = args.EntityManager.System(); + + //hide your children, it's time to figure out whose blood is in this shit + if (args is EntityEffectReagentArgs reagentArgs) + { + //get all DNAs stored in the injected solution + List dnaDataList = new List(); + if (reagentArgs.Source != null) + { + foreach (var reagent in reagentArgs.Source.Contents) + { + foreach (var data in reagent.Reagent.EnsureReagentData()) + { + if (data is DnaData) + { + dnaDataList.Add(((DnaData)data)); + } + } + } + //we have all the DNA in the activated subjuzine. get a random one and find the DNA's source. + if (dnaDataList.Count > 0) + { + DnaData chosenOne = dnaDataList[0]; + + String chosenName = forensicSys.GetNameFromDNA(chosenOne.DNA); + //we FINALLY have the name of the injector. jesus fuck. + //now, we build the role name, description, etc. + + //Don't add a ghost role to things that already have ghost roles + + String rules = (Loc.GetString("ghost-role-information-subjuzine-rules-1")); + rules = rules + chosenName; + rules = rules + (Loc.GetString("ghost-role-information-subjuzine-rules-2")); + + if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole)) + { + //if there already was a ghost role, change the role description and rules to make it clear it's been injected with subjuzine + ghostRole = entityManager.GetComponent(uid); + ghostRole.RoleDescription = Loc.GetString("ghost-role-information-subjuzine-description"); + ghostRole.RoleRules = rules; + return; + } + + ghostRole = entityManager.AddComponent(uid); + entityManager.EnsureComponent(uid); + + var entityData = entityManager.GetComponent(uid); + ghostRole.RoleName = entityData.EntityName; + ghostRole.RoleDescription = Loc.GetString("ghost-role-information-subjuzine-description"); + ghostRole.RoleRules = rules; + + + } + else //if there's no DNA in the DNA list, just act as if it was normal cognizine. + { + //Don't add a ghost role to things that already have ghost roles + if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole)) + { + return; + } + + ghostRole = entityManager.AddComponent(uid); + entityManager.EnsureComponent(uid); + + var entityData = entityManager.GetComponent(uid); + ghostRole.RoleName = entityData.EntityName; + ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description"); + } + } + } + } +} diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index 34529aba4a2f7d..2ce83e7584d7e1 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -17,6 +17,7 @@ using Robust.Shared.Random; using Content.Shared.Verbs; using Robust.Shared.Utility; +using Robust.Shared.GameObjects; namespace Content.Server.Forensics { @@ -134,6 +135,26 @@ public void CopyForensicsFrom(ForensicsComponent src, EntityUid target) dest.Fingerprints.Add(print); } } + public string GetNameFromDNA(string DNA) + { + var query = EntityQueryEnumerator(); + + String outputName = "OH GOD OH FUCK IT'S BROKEN"; + //iterate over every DNAcomponent in the server until you find one that matches the given DNA + while (query.MoveNext(out var sourceUID, out var sourceComp)) + { + if (sourceComp.DNA.Equals(DNA)) + { + + if (EntityManager.TryGetComponent(sourceUID, out MetaDataComponent? metaData)) + { + //output the name of the entity with the given DNA + outputName = metaData.EntityName; + } + } + } + return outputName; + } public List GetSolutionsDNA(EntityUid uid) { diff --git a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs index 534b6ba9f6b31b..f8a1fa7c91b6ae 100644 --- a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs +++ b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs @@ -166,8 +166,27 @@ private List PerformReaction(Entity soln, ReactionPro var energy = reaction.ConserveEnergy ? solution.GetThermalEnergy(_prototypeManager) : 0; + List dnaDataList = new List(); + + //save reactant DNA to DNAlist + if (reaction.PreserveDNA) + { + + foreach (var reagent in solution.Contents) + { + + foreach (var data in reagent.Reagent.EnsureReagentData()) + { + if (data is DnaData) + { + dnaDataList.Add((data)); + } + } + } + } + //Remove reactants - foreach (var reactant in reaction.Reactants) + foreach (KeyValuePair reactant in reaction.Reactants) { if (!reactant.Value.Catalyst) { @@ -181,7 +200,7 @@ private List PerformReaction(Entity soln, ReactionPro foreach (var product in reaction.Products) { products.Add(product.Key); - solution.AddReagent(product.Key, product.Value * unitReactions); + solution.AddReagent(new ReagentId(product.Key, dnaDataList), product.Value * unitReactions); } if (reaction.ConserveEnergy) @@ -191,6 +210,7 @@ private List PerformReaction(Entity soln, ReactionPro solution.Temperature = energy / newCap; } + OnReaction(soln, reaction, null, unitReactions); return products; diff --git a/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs b/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs index 351aa50b0f3fe8..0d19fa56adb1dc 100644 --- a/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs +++ b/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs @@ -62,6 +62,12 @@ public sealed partial class ReactionPrototype : IPrototype, IComparable [DataField("effects", serverOnly: true)] public List Effects = new(); + /// + /// If true, this reaction will attempt to transfer any DNA from the input chemicals to the output chemical. + /// + [DataField("preserveDNA")] + public bool PreserveDNA = false; + /// /// How dangerous is this effect? Stuff like bicaridine should be low, while things like methamphetamine /// or potas/water should be high. diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index ad5d23a8960f58..ccaa009d9db855 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -61,6 +61,13 @@ ghost-role-information-giant-spider-rules = You are a [color=red][bold]Team Anta ghost-role-information-cognizine-description = Made conscious with the magic of cognizine. +ghost-role-information-subjuzine-description = Made obedient with the magic of subjuzine. +ghost-role-information-subjuzine-rules-1 = You are a [color=#6495ed][bold]Familiar[/bold][/color] under the control of [color=red][bold] +ghost-role-information-subjuzine-rules-2 = [/bold][/color]. Follow your master's orders and keep their identity secret. + You don't remember any of your previous life, and you don't remember anything you learned as a ghost. + You are allowed to remember knowledge about the game in general, such as how to cook, how to use objects, etc. + You are absolutely [color=red]NOT[/color] allowed to remember, say, the name, appearance, etc. of your previous character. + ghost-role-information-hamster-name = Hamster ghost-role-information-hamster-description = A grumpy little ball of fluff. diff --git a/Resources/Locale/en-US/paper/paper-misc.ftl b/Resources/Locale/en-US/paper/paper-misc.ftl index 8d552d099eee0d..5a71c4d843eb37 100644 --- a/Resources/Locale/en-US/paper/paper-misc.ftl +++ b/Resources/Locale/en-US/paper/paper-misc.ftl @@ -53,6 +53,12 @@ book-text-agrichemkit-manual = Thank you for choosing the safe-for-all-ages Nano Unstable mutagen is entirely safe when used as a fertilizer, and NanoTrasen takes no responsibility for dead crops, excessive water bills, newly sentient plants asking existential questions, or flora-strangled farmhands that may coincidentally occur while using it. Do not drink unstable mutagen. Wash your hands thoroughly after handing. Wash your eyes if you have looked at unstable mutagen for over 30 minutes in a 24 hour period. Store in a dark room between 293–295K. Do not use on corporate holidays. If you begin hearing voices telling you to drink unstable mutagen, please contact your doctor, head of personnel, or exorcist. + +book-text-animal-friends-kit = Thank you for choosing the Animal Friends Kit! + Enclosed is a vial of dormant Subjuzine. Use the included syringe to draw 5 units of your blood, and put it into the vial to activate! + Injecting any non-sentient creature with Subjuzine will not only force them to follow your commands, but give them the gift of speech! + Good luck, Agent. Go make some animal friends! + book-text-combat-bakery-kit = Thank you for choosing our combat bakery kit! Enclosed are two (2) CyberSun patented Throwing Croissants, and one (1) patent-pending Baguette Sword. The included Donk Co. microwave board can construct a microwave capable of baking more weapons. diff --git a/Resources/Locale/en-US/reagents/meta/medicine.ftl b/Resources/Locale/en-US/reagents/meta/medicine.ftl index 3d11e82f9149dc..5b4e0d18e1e62a 100644 --- a/Resources/Locale/en-US/reagents/meta/medicine.ftl +++ b/Resources/Locale/en-US/reagents/meta/medicine.ftl @@ -97,6 +97,9 @@ reagent-desc-ethylredoxrazine = Neutralises the effects of alcohol in the blood reagent-name-cognizine = cognizine reagent-desc-cognizine = A mysterious chemical which is able to make any non-sentient creature sentient. +reagent-name-subjuzine = subjuzine +reagent-desc-subjuzine = A devious alteration of cognizine that bends the creature's will to that of whoever injected them. + reagent-name-ethyloxyephedrine = ethyloxyephedrine reagent-desc-ethyloxyephedrine = A mildly unstable medicine derived from desoxyephedrine, primarily used to combat narcolepsy. diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index fbb41ad86ef7e9..5a1febe1809eb5 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -115,6 +115,10 @@ uplink-holoclown-kit-desc = A joint venture between Cybersun and Honk.co. Contai uplink-holster-name = Shoulder Holster uplink-holster-desc = A deep shoulder holster capable of holding many types of ballistics. +uplink-animal-friends-kit-name = Animal Friends Kit +uplink-animal-friends-kit-desc = A box containing 4 doses of subjuzine, an alteration of cognizine that forces the injected creature to your will. Also contains an empty syringe and two mice to use it on. + + uplink-chest-rig-name = Chest Rig uplink-chest-rig-desc = Explosion-resistant tactical webbing used for holding traitor goods. diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml b/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml index 0f99ae77d47fce..ab926305c89989 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/syndicate.yml @@ -68,6 +68,24 @@ - id: DeathRattleImplanter amount: 6 +- type: entity + parent: [BoxCardboard, BaseSyndicateContraband] + id: AnimalFriendsKit + name: animal friends kit + description: Contains 5 doses of Subjuzine to convert animals into your henchmen. Just add blood! + components: + - type: Sprite + layers: + - state: box_of_doom + - state: syringe + - type: StorageFill + contents: + - id: MouseCube + amount: 2 + - id: Syringe + - id: SubjuzineChemistryVial + - id: PaperWrittenAnimalFriendsKit + - type: entity parent: [BoxCardboard, BaseSyndicateContraband] id: CombatBakeryKit diff --git a/Resources/Prototypes/Catalog/Fills/Paper/manuals.yml b/Resources/Prototypes/Catalog/Fills/Paper/manuals.yml index 4da9ec51878cd2..3e77ece6922839 100644 --- a/Resources/Prototypes/Catalog/Fills/Paper/manuals.yml +++ b/Resources/Prototypes/Catalog/Fills/Paper/manuals.yml @@ -33,6 +33,15 @@ - type: Paper content: book-text-agrichemkit-manual +- type: entity + id: PaperWrittenAnimalFriendsKit + name: "animal friends kit instructions" + description: A small, handwritten note detailing how to use the animal friends kit. + parent: Paper + components: + - type: Paper + content: book-text-animal-friends-kit + - type: entity id: PaperWrittenCombatBakeryKit name: "combat bakery kit instructions" @@ -40,4 +49,4 @@ parent: Paper components: - type: Paper - content: book-text-combat-bakery-kit \ No newline at end of file + content: book-text-combat-bakery-kit diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index aa6aca9c842f7d..570eb12016692b 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1141,6 +1141,17 @@ tags: - NukeOpsUplink +- type: listing + id: UplinkAnimalFriendsKit + name: uplink-animal-friends-kit-name + description: uplink-animal-friends-kit-desc + icon: { sprite: /Textures/Mobs/Animals/mouse.rsi, state: icon-2 } + productEntity: AnimalFriendsKit + cost: + Telecrystal: 6 + categories: + - UplinkAllies + - type: listing id: UplinkReinforcementRadioSyndicate name: uplink-reinforcement-radio-name diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml index ec910150eec05f..56a3bea4818f98 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml @@ -149,3 +149,18 @@ reagents: - ReagentId: Chlorine Quantity: 5 + +- type: entity + id: SubjuzineChemistryVial + name: subjuzine vial + parent: BaseChemistryEmptyVial + components: + - type: SolutionContainerManager + solutions: + beaker: + maxVol: 30 + reagents: + - ReagentId: Subjuzine + Quantity: 20 + - type: Tag + tags: [] diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 4714bd35ed200a..460f25092f7f09 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -920,6 +920,40 @@ - !type:ReagentThreshold min: 5 +- type: reagent + id: Subjuzine + name: reagent-name-subjuzine + desc: reagent-desc-subjuzine + group: Medicine + physicalDesc: reagent-physical-desc-enigmatic + flavor: magical + color: "#a50ed8" + metabolisms: + Medicine: + effects: + - !type:MakeSentient + conditions: + - !type:ReagentThreshold + min: 5 + + +- type: reagent + id: ActivatedSubjuzine + name: reagent-name-subjuzine + desc: reagent-desc-subjuzine + group: Medicine + physicalDesc: reagent-physical-desc-enigmatic + flavor: magical + color: "#911d06" + metabolisms: + Medicine: + effects: + - !type:MakeSyndient + conditions: + - !type:ReagentThreshold + min: 5 + + - type: reagent id: Ethyloxyephedrine name: reagent-name-ethyloxyephedrine diff --git a/Resources/Prototypes/Recipes/Reactions/drinks.yml b/Resources/Prototypes/Recipes/Reactions/drinks.yml index 46663f7a18796a..d68363ddc22acc 100644 --- a/Resources/Prototypes/Recipes/Reactions/drinks.yml +++ b/Resources/Prototypes/Recipes/Reactions/drinks.yml @@ -294,6 +294,7 @@ - type: reaction id: DemonsBlood + preserveDNA: true requiredMixerCategories: - Stir reactants: @@ -310,6 +311,7 @@ - type: reaction id: DevilsKiss + preserveDNA: true requiredMixerCategories: - Stir reactants: @@ -824,6 +826,7 @@ - type: reaction id: RedMead + preserveDNA: true reactants: Mead: amount: 1 diff --git a/Resources/Prototypes/Recipes/Reactions/fun.yml b/Resources/Prototypes/Recipes/Reactions/fun.yml index 6c107495453154..294057724355f8 100644 --- a/Resources/Prototypes/Recipes/Reactions/fun.yml +++ b/Resources/Prototypes/Recipes/Reactions/fun.yml @@ -87,6 +87,7 @@ - type: reaction id: SpaceGlue + preserveDNA: true minTemp: 370 reactants: SpaceLube: diff --git a/Resources/Prototypes/Recipes/Reactions/medicine.yml b/Resources/Prototypes/Recipes/Reactions/medicine.yml index 505d86832f6349..137c283464905e 100644 --- a/Resources/Prototypes/Recipes/Reactions/medicine.yml +++ b/Resources/Prototypes/Recipes/Reactions/medicine.yml @@ -208,6 +208,7 @@ - type: reaction id: Ambuzol + preserveDNA: true reactants: Dylovene: amount: 1 @@ -380,6 +381,82 @@ products: Cognizine: 1 +- type: reaction + id: Subjuzine + reactants: + Cognizine: + amount: 1 + Vestine: + amount: 1 + products: + Subjuzine: 2 + +- type: reaction + id: Subjuzine-Activation-Blood + preserveDNA: true + reactants: + Subjuzine: + amount: 4 + Blood: + amount: 1 + products: + ActivatedSubjuzine: 5 + +- type: reaction + id: Subjuzine-Activation-Slime + preserveDNA: true + reactants: + Subjuzine: + amount: 4 + Slime: + amount: 1 + products: + ActivatedSubjuzine: 5 + +- type: reaction + id: Subjuzine-Activation-Insect + preserveDNA: true + reactants: + Subjuzine: + amount: 4 + InsectBlood: + amount: 1 + products: + ActivatedSubjuzine: 5 + +- type: reaction + id: Subjuzine-Activation-Sap + preserveDNA: true + reactants: + Subjuzine: + amount: 4 + Sap: + amount: 1 + products: + ActivatedSubjuzine: 5 + +- type: reaction + id: Subjuzine-Activation-BlueBlood + preserveDNA: true + reactants: + Subjuzine: + amount: 4 + CopperBlood: + amount: 1 + products: + ActivatedSubjuzine: 5 + +- type: reaction + id: Subjuzine-Activation-Anaerobic + preserveDNA: true + reactants: + Subjuzine: + amount: 4 + AmmoniaBlood: + amount: 1 + products: + ActivatedSubjuzine: 5 + - type: reaction id: Sigynate impact: Medium diff --git a/Resources/Prototypes/Recipes/Reactions/single_reagent.yml b/Resources/Prototypes/Recipes/Reactions/single_reagent.yml index 46fe35415f0b62..ad4b67d9a53b3a 100644 --- a/Resources/Prototypes/Recipes/Reactions/single_reagent.yml +++ b/Resources/Prototypes/Recipes/Reactions/single_reagent.yml @@ -37,6 +37,7 @@ - type: reaction id: BloodToWine + preserveDNA: true impact: Low requiredMixerCategories: - Holy