]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Added a component to eat bodies for health #8922 (#16345)
authorPilgrimViis <PilgrimViis@users.noreply.github.com>
Mon, 15 May 2023 22:44:35 +0000 (00:44 +0200)
committerGitHub <noreply@github.com>
Mon, 15 May 2023 22:44:35 +0000 (18:44 -0400)
Content.Client/Devour/DevourSystem.cs [new file with mode: 0644]
Content.Server/Devour/DevourSystem.cs [new file with mode: 0644]
Content.Server/Dragon/Components/DragonComponent.cs
Content.Server/Dragon/DragonSystem.cs
Content.Shared/Devour/Components/DevourerComponent.cs [new file with mode: 0644]
Content.Shared/Devour/SharedDevourSystem.cs [new file with mode: 0644]
Content.Shared/Dragon/DragonDevourDoAfterEvent.cs [deleted file]
Resources/Prototypes/Entities/Mobs/Player/dragon.yml

diff --git a/Content.Client/Devour/DevourSystem.cs b/Content.Client/Devour/DevourSystem.cs
new file mode 100644 (file)
index 0000000..ad905ff
--- /dev/null
@@ -0,0 +1,6 @@
+using Content.Shared.Devour;
+
+namespace Content.Client.Devour;
+public sealed class DevourSystem : SharedDevourSystem
+{
+}
diff --git a/Content.Server/Devour/DevourSystem.cs b/Content.Server/Devour/DevourSystem.cs
new file mode 100644 (file)
index 0000000..5421751
--- /dev/null
@@ -0,0 +1,51 @@
+using Content.Shared.Devour;
+using Content.Server.Body.Systems;
+using Content.Shared.Humanoid;
+using Content.Shared.Chemistry.Components;
+using Content.Server.Devour.Components;
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+
+namespace Content.Server.Devour;
+
+public sealed class DevourSystem : SharedDevourSystem
+{
+    [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<DevourerComponent, DevourDoAfterEvent>(OnDoAfter);
+    }
+
+    private void OnDoAfter(EntityUid uid, DevourerComponent component, DevourDoAfterEvent args)
+    {
+        if (args.Handled || args.Cancelled)
+            return;
+
+        var ichorInjection = new Solution(component.Chemical, component.HealRate);
+
+        if (component.FoodPreference == FoodPreference.All ||
+            (component.FoodPreference == FoodPreference.Humanoid && HasComp<HumanoidAppearanceComponent>(args.Args.Target)))
+        {
+            ichorInjection.ScaleSolution(0.5f);
+
+            if (component.ShouldStoreDevoured && args.Args.Target is not null)
+            {
+                component.Stomach.Insert(args.Args.Target.Value);
+            }
+            _bloodstreamSystem.TryAddToChemicals(uid, ichorInjection);
+        }
+
+        //TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow.
+        //If it's not human, it must be a structure
+        else if (args.Args.Target != null)
+        {
+            QueueDel(args.Args.Target.Value);
+        }
+
+        _audioSystem.PlayPvs(component.SoundDevour, uid);
+    }
+}
+
index 2222b64a158d6588934bb7675ac2839f44cc6d13..430b5d5fc220a0de297a9509b35cf2bbc74de87b 100644 (file)
@@ -13,23 +13,6 @@ namespace Content.Server.Dragon
     [RegisterComponent]
     public sealed class DragonComponent : Component
     {
-        /// <summary>
-        /// The chemical ID injected upon devouring
-        /// </summary>
-        [DataField("devourChemical", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
-        public string DevourChem = "Ichor";
-
-        /// <summary>
-        /// The amount of ichor injected per devour
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite), DataField("devourHealRate")]
-        public float DevourHealRate = 15f;
-
-        [DataField("devourActionId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityTargetActionPrototype>))]
-        public string DevourActionId = "DragonDevour";
-
-        [DataField("devourAction")]
-        public EntityTargetAction? DevourAction;
 
         /// <summary>
         /// If we have active rifts.
@@ -68,58 +51,15 @@ namespace Content.Server.Dragon
         [ViewVariables(VVAccess.ReadWrite), DataField("riftPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
         public string RiftPrototype = "CarpRift";
 
-        /// <summary>
-        /// The amount of time it takes to devour something
-        /// <remarks>
-        /// NOTE: original intended design was to increase this proportionally with damage thresholds, but those proved quite difficult to get consistently. right now it devours the structure at a fixed timer.
-        /// </remarks>
-        /// </summary>
-        [DataField("structureDevourTime")]
-        public float StructureDevourTime = 10f;
-
-        [DataField("devourTime")]
-        public float DevourTime = 3f;
-
         [ViewVariables(VVAccess.ReadWrite), DataField("soundDeath")]
         public SoundSpecifier? SoundDeath = new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg");
 
-        [ViewVariables(VVAccess.ReadWrite), DataField("soundDevour")]
-        public SoundSpecifier? SoundDevour = new SoundPathSpecifier("/Audio/Effects/demon_consume.ogg")
-        {
-            Params = AudioParams.Default.WithVolume(-3f),
-        };
-
-        [ViewVariables(VVAccess.ReadWrite), DataField("soundStructureDevour")]
-        public SoundSpecifier? SoundStructureDevour = new SoundPathSpecifier("/Audio/Machines/airlock_creaking.ogg")
-        {
-            Params = AudioParams.Default.WithVolume(-3f),
-        };
-
         [ViewVariables(VVAccess.ReadWrite), DataField("soundRoar")]
         public SoundSpecifier? SoundRoar =
             new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg")
             {
                 Params = AudioParams.Default.WithVolume(3f),
             };
-
-        [ViewVariables(VVAccess.ReadWrite), DataField("devourWhitelist")]
-        public EntityWhitelist? DevourWhitelist = new()
-        {
-            Components = new[]
-            {
-                "Door",
-                "MobState",
-            },
-            Tags = new List<string>
-            {
-                "Wall",
-            },
-        };
-
-        /// <summary>
-        /// Where the entities go when dragon devours them, empties when the dragon is butchered.
-        /// </summary>
-        public Container DragonStomach = default!;
     }
 
     public sealed class DragonDevourActionEvent : EntityTargetActionEvent {}
index 329a4379f65c1f2090cda5ff80b9339e1bdfab3c..befdc50ca4957a90eb787d0cb12f023dff3224b8 100644 (file)
@@ -59,12 +59,9 @@ namespace Content.Server.Dragon
 
             SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
             SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
-            SubscribeLocalEvent<DragonComponent, DragonDevourActionEvent>(OnDevourAction);
             SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
             SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
 
-            SubscribeLocalEvent<DragonComponent, DragonDevourDoAfterEvent>(OnDoAfter);
-
             SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
 
             SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnRiftShutdown);
@@ -75,29 +72,6 @@ namespace Content.Server.Dragon
             SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
         }
 
-        private void OnDoAfter(EntityUid uid, DragonComponent component, DragonDevourDoAfterEvent args)
-        {
-            if (args.Handled || args.Cancelled)
-                return;
-
-            var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
-
-            //Humanoid devours allow dragon to get eggs, corpses included
-            if (HasComp<HumanoidAppearanceComponent>(args.Args.Target))
-            {
-                ichorInjection.ScaleSolution(0.5f);
-                component.DragonStomach.Insert(args.Args.Target.Value);
-                _bloodstreamSystem.TryAddToChemicals(uid, ichorInjection);
-            }
-
-            //TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow.
-            //If it's not human, it must be a structure
-            else if (args.Args.Target != null)
-                EntityManager.QueueDeleteEntity(args.Args.Target.Value);
-
-            _audioSystem.PlayPvs(component.SoundDevour, uid);
-        }
-
         public override void Update(float frameTime)
         {
             base.Update(frameTime);
@@ -303,8 +277,6 @@ namespace Content.Server.Dragon
                 if (component.SoundDeath != null)
                     _audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params);
 
-                component.DragonStomach.EmptyContainer();
-
                 foreach (var rift in component.Rifts)
                 {
                     QueueDel(rift);
@@ -322,62 +294,10 @@ namespace Content.Server.Dragon
 
         private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
         {
-            //Dragon doesn't actually chew, since he sends targets right into his stomach.
-            //I did it mom, I added ERP content into upstream. Legally!
-            component.DragonStomach = _containerSystem.EnsureContainer<Container>(uid, "dragon_stomach");
-
-            if (component.DevourAction != null)
-                _actionsSystem.AddAction(uid, component.DevourAction, null);
-
             if (component.SpawnRiftAction != null)
                 _actionsSystem.AddAction(uid, component.SpawnRiftAction, null);
 
             Roar(component);
         }
-
-        /// <summary>
-        /// The devour action
-        /// </summary>
-        private void OnDevourAction(EntityUid uid, DragonComponent component, DragonDevourActionEvent args)
-        {
-            if (args.Handled || component.DevourWhitelist?.IsValid(args.Target, EntityManager) != true)
-                return;
-
-            args.Handled = true;
-            var target = args.Target;
-
-            // Structure and mob devours handled differently.
-            if (EntityManager.TryGetComponent(target, out MobStateComponent? targetState))
-            {
-                switch (targetState.CurrentState)
-                {
-                    case MobState.Critical:
-                    case MobState.Dead:
-
-                        _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.DevourTime, new DragonDevourDoAfterEvent(), uid, target: target, used: uid)
-                        {
-                            BreakOnTargetMove = true,
-                            BreakOnUserMove = true,
-                        });
-                        break;
-                    default:
-                        _popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-fail-target-alive"), uid, uid);
-                        break;
-                }
-
-                return;
-            }
-
-            _popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-structure"), uid, uid);
-
-            if (component.SoundStructureDevour != null)
-                _audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params);
-
-            _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.StructureDevourTime, new DragonDevourDoAfterEvent(), uid, target: target, used: uid)
-            {
-                BreakOnTargetMove = true,
-                BreakOnUserMove = true,
-            });
-        }
     }
 }
diff --git a/Content.Shared/Devour/Components/DevourerComponent.cs b/Content.Shared/Devour/Components/DevourerComponent.cs
new file mode 100644 (file)
index 0000000..97085ca
--- /dev/null
@@ -0,0 +1,82 @@
+using Content.Shared.Actions;
+using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Devour;
+using Content.Shared.Whitelist;
+using Robust.Shared.Audio;
+using Robust.Shared.Containers;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.Devour.Components;
+
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedDevourSystem))]
+public sealed class DevourerComponent : Component
+{
+    [DataField("devourActionId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityTargetActionPrototype>))]
+    public string DevourActionId = "Devour";
+
+    [DataField("devourAction")]
+    public EntityTargetAction? DevourAction;
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("soundDevour")]
+    public SoundSpecifier? SoundDevour = new SoundPathSpecifier("/Audio/Effects/demon_consume.ogg")
+    {
+        Params = AudioParams.Default.WithVolume(-3f),
+    };
+
+    [DataField("devourTime")]
+    public float DevourTime = 3f;
+
+    /// <summary>
+    /// The amount of time it takes to devour something
+    /// <remarks>
+    /// NOTE: original intended design was to increase this proportionally with damage thresholds, but those proved quite difficult to get consistently. right now it devours the structure at a fixed timer.
+    /// </remarks>
+    /// </summary>
+    [DataField("structureDevourTime")]
+    public float StructureDevourTime = 10f;
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("soundStructureDevour")]
+    public SoundSpecifier? SoundStructureDevour = new SoundPathSpecifier("/Audio/Machines/airlock_creaking.ogg")
+    {
+        Params = AudioParams.Default.WithVolume(-3f),
+    };
+
+    /// <summary>
+    /// Where the entities go when it devours them, empties when it is butchered.
+    /// </summary>
+    public Container Stomach = default!;
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("shouldStoreDevoured")]
+    public bool ShouldStoreDevoured = true;
+
+    [ViewVariables(VVAccess.ReadWrite), DataField("whitelist")]
+    public EntityWhitelist? Whitelist = new()
+    {
+        Components = new[]
+        {
+            "MobState",
+        }
+    };
+
+    /// <summary>
+    /// The chemical ID injected upon devouring
+    /// </summary>
+    [DataField("chemical", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
+    public string Chemical = "Ichor";
+
+    /// <summary>
+    /// The amount of ichor injected per devour
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("healRate")]
+    public float HealRate = 15f;
+
+    /// <summary>
+    /// The favorite food not only feeds you, but also heals
+    /// </summary>
+    [DataField("foodPreference")]
+    public FoodPreference FoodPreference = FoodPreference.All;
+}
+
diff --git a/Content.Shared/Devour/SharedDevourSystem.cs b/Content.Shared/Devour/SharedDevourSystem.cs
new file mode 100644 (file)
index 0000000..117d010
--- /dev/null
@@ -0,0 +1,94 @@
+using Content.Shared.DoAfter;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs;
+using Robust.Shared.Containers;
+using Content.Server.Devour.Components;
+using Content.Shared.Actions;
+using Content.Shared.Popups;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Devour;
+
+public abstract class SharedDevourSystem : EntitySystem
+{
+    [Dependency] protected readonly SharedAudioSystem _audioSystem = default!;
+    [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+    [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+    [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
+    [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<DevourerComponent, ComponentStartup>(OnStartup);
+        SubscribeLocalEvent<DevourerComponent, DevourActionEvent>(OnDevourAction);
+    }
+
+    protected void OnStartup(EntityUid uid, DevourerComponent component, ComponentStartup args)
+    {
+        //Devourer doesn't actually chew, since he sends targets right into his stomach.
+        //I did it mom, I added ERP content into upstream. Legally!
+        component.Stomach = _containerSystem.EnsureContainer<Container>(uid, "stomach");
+
+        if (component.DevourAction != null)
+            _actionsSystem.AddAction(uid, component.DevourAction, null);
+    }
+
+    /// <summary>
+    /// The devour action
+    /// </summary>
+    protected void OnDevourAction(EntityUid uid, DevourerComponent component, DevourActionEvent args)
+    {
+        if (args.Handled || component.Whitelist?.IsValid(args.Target, EntityManager) != true)
+            return;
+
+        args.Handled = true;
+        var target = args.Target;
+
+        // Structure and mob devours handled differently.
+        if (TryComp(target, out MobStateComponent? targetState))
+        {
+            switch (targetState.CurrentState)
+            {
+                case MobState.Critical:
+                case MobState.Dead:
+
+                    _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.DevourTime, new DevourDoAfterEvent(), uid, target: target, used: uid)
+                    {
+                        BreakOnTargetMove = true,
+                        BreakOnUserMove = true,
+                    });
+                    break;
+                default:
+                    _popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-fail-target-alive"), uid, uid);
+                    break;
+            }
+
+            return;
+        }
+
+        _popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-structure"), uid, uid);
+
+        if (component.SoundStructureDevour != null)
+            _audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params);
+
+        _doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.StructureDevourTime, new DevourDoAfterEvent(), uid, target: target, used: uid)
+        {
+            BreakOnTargetMove = true,
+            BreakOnUserMove = true,
+        });
+    }
+}
+
+public sealed class DevourActionEvent : EntityTargetActionEvent { }
+
+[Serializable, NetSerializable]
+public sealed class DevourDoAfterEvent : SimpleDoAfterEvent { }
+
+[Serializable, NetSerializable]
+public enum FoodPreference : byte
+{
+    Humanoid = 0,
+    All = 1
+}
diff --git a/Content.Shared/Dragon/DragonDevourDoAfterEvent.cs b/Content.Shared/Dragon/DragonDevourDoAfterEvent.cs
deleted file mode 100644 (file)
index 2f5f0d5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-using Content.Shared.DoAfter;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Dragon;
-
-[Serializable, NetSerializable]
-public sealed class DragonDevourDoAfterEvent : SimpleDoAfterEvent
-{
-}
\ No newline at end of file
index d179fbe6153f903d4f269d2826aeee04ee8308ee..5abd832bc0ba5d5eee4ac57cc56505e53578c061 100644 (file)
         types:
           Piercing: 15
           Slash: 15
-    - type: Dragon
-      spawnsLeft: 2
-      spawnsProto: MobCarpDragon
+    - type: Devourer
+      foodPreference: Humanoid
+      shouldStoreDevoured: true
+      chemical: Ichor
+      healRate: 15.0
+      whitelist:
+        components:
+          - MobState
+          - Door
+        tags:
+          - Wall
       devourAction:
-        event: !type:DragonDevourActionEvent
+        event: !type:DevourActionEvent
         icon: Interface/Actions/devour.png
         name: action-name-devour
         description: action-description-devour
-        devourChemical: Ichor
-        devourHealRate: 15.0
-        whitelist:
-          components:
-            - MobState
-            - Door
-          tags:
-            - Wall
+    - type: Dragon
+      spawnsLeft: 2
+      spawnsProto: MobCarpDragon
       spawnRiftAction:
         event: !type:DragonSpawnRiftActionEvent
         icon: