]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Sentient medibot now can inject (#32110)
authorgodisdeadLOL <169250097+godisdeadLOL@users.noreply.github.com>
Wed, 29 Jan 2025 10:34:01 +0000 (13:34 +0300)
committerGitHub <noreply@github.com>
Wed, 29 Jan 2025 10:34:01 +0000 (13:34 +0300)
medibot player injection

Co-authored-by: YourUsername <you@example.com>
Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/MedibotInjectOperator.cs
Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickNearbyInjectableOperator.cs
Content.Server/NPC/Systems/NPCPerceptionSystem.RecentlyInjected.cs
Content.Shared/NPC/Components/NPCRecentlyInjectedComponent.cs [moved from Content.Server/NPC/Components/NPCRecentlyInjectedComponent.cs with 78% similarity]
Content.Shared/Silicons/Bots/MedibotSystem.cs
Resources/Locale/en-US/npc/medibot.ftl
Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml

index 2cc735194f6a65ab397c450ce70ccf26acf40d41..bac9cfcf40b476065437d2cf1e6caca109a32e87 100644 (file)
@@ -1,5 +1,5 @@
 using Content.Server.Chat.Systems;
-using Content.Server.NPC.Components;
+using Content.Shared.NPC.Components;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Damage;
 using Content.Shared.Emag.Components;
@@ -55,34 +55,11 @@ public sealed partial class MedibotInjectOperator : HTNOperator
         if (!_entMan.TryGetComponent<MedibotComponent>(owner, out var botComp))
             return HTNOperatorStatus.Failed;
 
-
-        if (!_entMan.TryGetComponent<DamageableComponent>(target, out var damage))
-            return HTNOperatorStatus.Failed;
-
-        if (!_solutionContainer.TryGetInjectableSolution(target, out var injectable, out _))
+        if (!_medibot.CheckInjectable((owner, botComp), target) || !_medibot.TryInject((owner, botComp), target))
             return HTNOperatorStatus.Failed;
 
-        if (!_interaction.InRangeUnobstructed(owner, target))
-            return HTNOperatorStatus.Failed;
-
-        var total = damage.TotalDamage;
-
-        // always inject healthy patients when emagged
-        if (total == 0 && !_entMan.HasComponent<EmaggedComponent>(owner))
-            return HTNOperatorStatus.Failed;
-
-        if (!_entMan.TryGetComponent<MobStateComponent>(target, out var mobState))
-            return HTNOperatorStatus.Failed;
-
-        var state = mobState.CurrentState;
-        if (!_medibot.TryGetTreatment(botComp, mobState.CurrentState, out var treatment) || !treatment.IsValid(total))
-            return HTNOperatorStatus.Failed;
-
-        _entMan.EnsureComponent<NPCRecentlyInjectedComponent>(target);
-        _solutionContainer.TryAddReagent(injectable.Value, treatment.Reagent, treatment.Quantity, out _);
-        _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, target);
-        _audio.PlayPvs(botComp.InjectSound, target);
         _chat.TrySendInGameICMessage(owner, Loc.GetString("medibot-finish-inject"), InGameICChatType.Speak, hideChat: true, hideLog: true);
+
         return HTNOperatorStatus.Finished;
     }
 }
index a71091ad97dd421560cdd19d45ec76f9535d7a54..f351d582c6eede65147c4f741b988c592c16febb 100644 (file)
@@ -1,6 +1,6 @@
 using System.Threading;
 using System.Threading.Tasks;
-using Content.Server.NPC.Components;
+using Content.Shared.NPC.Components;
 using Content.Server.NPC.Pathfinding;
 using Content.Shared.Chemistry.Components.SolutionManager;
 using Content.Shared.Damage;
index dd4ff6c65f19fd4a675ec8144cb3bc564fc6ba2f..6d8cb9dfe54eed93e1ff7087aaefeea83464e07a 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Server.NPC.Components;
+using Content.Shared.NPC.Components;
 
 namespace Content.Server.NPC.Systems;
 
similarity index 78%
rename from Content.Server/NPC/Components/NPCRecentlyInjectedComponent.cs
rename to Content.Shared/NPC/Components/NPCRecentlyInjectedComponent.cs
index 0bc5dce55b9f83b4efbf684de03a59801db6eca9..b79c4dd9ef05a9337f8516616576b05989d38ca2 100644 (file)
@@ -1,8 +1,10 @@
-namespace Content.Server.NPC.Components
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.NPC.Components
 {
     /// Added when a medibot injects someone
     /// So they don't get injected again for at least a minute.
-    [RegisterComponent]
+    [RegisterComponent, NetworkedComponent]
     public sealed partial class NPCRecentlyInjectedComponent : Component
     {
         [ViewVariables(VVAccess.ReadWrite), DataField("accumulator")]
index 3ab73149c08e483e4ce3ab33963697017c1220b2..68f930931c2ef28d96cee7d7a2c47071b8084018 100644 (file)
@@ -1,6 +1,15 @@
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Damage;
+using Content.Shared.DoAfter;
+using Content.Shared.Emag.Components;
 using Content.Shared.Emag.Systems;
+using Content.Shared.Interaction;
 using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Content.Shared.NPC.Components;
+using Content.Shared.Popups;
 using Robust.Shared.Audio.Systems;
+using Robust.Shared.Serialization;
 using System.Diagnostics.CodeAnalysis;
 
 namespace Content.Shared.Silicons.Bots;
@@ -11,12 +20,18 @@ namespace Content.Shared.Silicons.Bots;
 public sealed class MedibotSystem : EntitySystem
 {
     [Dependency] private readonly SharedAudioSystem _audio = default!;
+    [Dependency] private SharedInteractionSystem _interaction = default!;
+    [Dependency] private SharedSolutionContainerSystem _solutionContainer = default!;
+    [Dependency] private SharedPopupSystem _popup = default!;
+    [Dependency] private SharedDoAfterSystem _doAfter = default!;
 
     public override void Initialize()
     {
         base.Initialize();
 
         SubscribeLocalEvent<EmaggableMedibotComponent, GotEmaggedEvent>(OnEmagged);
+        SubscribeLocalEvent<MedibotComponent, UserActivateInWorldEvent>(OnInteract);
+        SubscribeLocalEvent<MedibotComponent, MedibotInjectDoAfterEvent>(OnInject);
     }
 
     private void OnEmagged(EntityUid uid, EmaggableMedibotComponent comp, ref GotEmaggedEvent args)
@@ -34,6 +49,25 @@ public sealed class MedibotSystem : EntitySystem
         args.Handled = true;
     }
 
+    private void OnInteract(Entity<MedibotComponent> medibot, ref UserActivateInWorldEvent args)
+    {
+        if (!CheckInjectable(medibot!, args.Target, true)) return;
+
+        _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, 2f, new MedibotInjectDoAfterEvent(), args.User, args.Target)
+        {
+            BlockDuplicate = true,
+            BreakOnMove = true,
+        });
+    }
+
+    private void OnInject(EntityUid uid, MedibotComponent comp, ref MedibotInjectDoAfterEvent args)
+    {
+        if (args.Cancelled) return;
+
+        if (args.Target is { } target)
+            TryInject(uid, target);
+    }
+
     /// <summary>
     /// Get a treatment for a given mob state.
     /// </summary>
@@ -44,4 +78,66 @@ public sealed class MedibotSystem : EntitySystem
     {
         return comp.Treatments.TryGetValue(state, out treatment);
     }
+
+    /// <summary>
+    /// Checks if the target can be injected.
+    /// </summary>
+    public bool CheckInjectable(Entity<MedibotComponent?> medibot, EntityUid target, bool manual = false)
+    {
+        if (!Resolve(medibot, ref medibot.Comp, false)) return false;
+
+        if (HasComp<NPCRecentlyInjectedComponent>(target))
+        {
+            _popup.PopupClient(Loc.GetString("medibot-recently-injected"), medibot, medibot);
+            return false;
+        }
+
+        if (!TryComp<MobStateComponent>(target, out var mobState)) return false;
+        if (!TryComp<DamageableComponent>(target, out var damageable)) return false;
+        if (!_solutionContainer.TryGetInjectableSolution(target, out _, out _)) return false;
+
+        if (mobState.CurrentState != MobState.Alive && mobState.CurrentState != MobState.Critical)
+        {
+            _popup.PopupClient(Loc.GetString("medibot-target-dead"), medibot, medibot);
+            return false;
+        }
+
+        var total = damageable.TotalDamage;
+        if (total == 0 && !HasComp<EmaggedComponent>(medibot))
+        {
+            _popup.PopupClient(Loc.GetString("medibot-target-healthy"), medibot, medibot);
+            return false;
+        }
+
+        if (!TryGetTreatment(medibot.Comp, mobState.CurrentState, out var treatment) || !treatment.IsValid(total) && !manual) return false;
+
+        return true;
+    }
+
+    /// <summary>
+    /// Tries to inject the target.
+    /// </summary>
+    public bool TryInject(Entity<MedibotComponent?> medibot, EntityUid target)
+    {
+        if (!Resolve(medibot, ref medibot.Comp, false)) return false;
+
+        if (!_interaction.InRangeUnobstructed(medibot.Owner, target)) return false;
+
+        if (!TryComp<MobStateComponent>(target, out var mobState)) return false;
+        if (!TryGetTreatment(medibot.Comp, mobState.CurrentState, out var treatment)) return false;
+        if (!_solutionContainer.TryGetInjectableSolution(target, out var injectable, out _)) return false;
+
+        EnsureComp<NPCRecentlyInjectedComponent>(target);
+        _solutionContainer.TryAddReagent(injectable.Value, treatment.Reagent, treatment.Quantity, out _);
+
+        _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, target);
+        _popup.PopupClient(Loc.GetString("medibot-target-injected"), medibot, medibot);
+
+        _audio.PlayPredicted(medibot.Comp.InjectSound, medibot, medibot);
+
+        return true;
+    }
 }
+
+[Serializable, NetSerializable]
+public sealed partial class MedibotInjectDoAfterEvent : SimpleDoAfterEvent { }
index b645220119802b4c7c56a4941c8bcd28963d7fef..79be6d371b2f3786b8ce72dd7e08a94eab0371be 100644 (file)
@@ -1,2 +1,7 @@
 medibot-start-inject = Hold still, please.
 medibot-finish-inject = All done.
+
+medibot-target-dead = The patient is dead.
+medibot-target-healthy = The patient is already healthy.
+medibot-target-injected = The patient was injected.
+medibot-recently-injected = The patient was recently injected.
index 682b220ca2f6ff7b2a5dcd6cb87ee6e4fccce0d2..6a6e4285ebb7b0ecab02f76b86921a963215a9e6 100644 (file)
     pack: MedibotAds
   - type: Inventory
     templateId: medibot
+  - type: DoAfter
 
 - type: entity
   parent: