]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
moths can eat goat wool (#21704)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Fri, 17 Nov 2023 08:51:51 +0000 (08:51 +0000)
committerGitHub <noreply@github.com>
Fri, 17 Nov 2023 08:51:51 +0000 (01:51 -0700)
* add Wooly system

* add RequireDead to Food

* minor fix+cleanup and fix repeating

* minor fix+cleanup and fix repeating

* make goat wooly

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Content.Server/Animals/Components/WoolyComponent.cs [new file with mode: 0644]
Content.Server/Animals/Systems/WoolySystem.cs [new file with mode: 0644]
Content.Server/Nutrition/Components/FoodComponent.cs
Content.Server/Nutrition/EntitySystems/FoodSystem.cs
Resources/Prototypes/Entities/Mobs/NPCs/animals.yml

diff --git a/Content.Server/Animals/Components/WoolyComponent.cs b/Content.Server/Animals/Components/WoolyComponent.cs
new file mode 100644 (file)
index 0000000..8db4497
--- /dev/null
@@ -0,0 +1,42 @@
+using Content.Server.Animals.Systems;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+/// <summary>
+/// Lets an animal grow a wool solution when not hungry.
+/// </summary>
+[RegisterComponent, Access(typeof(WoolySystem))]
+public sealed partial class WoolyComponent : Component
+{
+    /// <summary>
+    /// What reagent to grow.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public ProtoId<ReagentPrototype> ReagentId = "Fiber";
+
+    /// <summary>
+    /// How much wool to grow at every growth cycle.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public FixedPoint2 Quantity = 25;
+
+    /// <summary>
+    /// What solution to add the wool reagent to.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "wool";
+
+    /// <summary>
+    /// How long to wait before growing wool.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1);
+
+    /// <summary>
+    /// When to next try growing wool.
+    /// </summary>
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+    public TimeSpan NextGrowth = TimeSpan.FromSeconds(0);
+}
diff --git a/Content.Server/Animals/Systems/WoolySystem.cs b/Content.Server/Animals/Systems/WoolySystem.cs
new file mode 100644 (file)
index 0000000..e63718c
--- /dev/null
@@ -0,0 +1,56 @@
+using Content.Server.Animals.Components;
+using Content.Server.Nutrition;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Nutrition.Components;
+using Content.Shared.Nutrition.EntitySystems;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Animals.Systems;
+
+/// <summary>
+/// Handles regeneration of an animal's wool solution when not hungry.
+/// Shearing is not currently possible so the only use is for moths to eat.
+/// </summary>
+public sealed class WoolySystem : EntitySystem
+{
+    [Dependency] private readonly HungerSystem _hunger = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<WoolyComponent, BeforeFullyEatenEvent>(OnBeforeFullyEaten);
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<WoolyComponent, HungerComponent>();
+        var now = _timing.CurTime;
+        while (query.MoveNext(out var uid, out var comp, out var hunger))
+        {
+            if (now < comp.NextGrowth)
+                continue;
+
+            comp.NextGrowth = now + comp.GrowthDelay;
+
+            // Is there enough nutrition to produce reagent?
+            if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Peckish)
+                continue;
+
+            if (!_solutionContainer.TryGetSolution(uid, comp.Solution, out var solution))
+                continue;
+
+            _solutionContainer.TryAddReagent(uid, solution, comp.ReagentId, comp.Quantity, out _);
+        }
+    }
+
+    private void OnBeforeFullyEaten(Entity<WoolyComponent> ent, ref BeforeFullyEatenEvent args)
+    {
+        // don't want moths to delete goats after eating them
+        args.Cancel();
+    }
+}
index af7435213215ca2551c868991e41f207f5212a56..c7f90ccff1da4cb90a03ad241837f17b4e728737 100644 (file)
@@ -67,4 +67,10 @@ public sealed partial class FoodComponent : Component
     /// </summary>
     [DataField]
     public float ForceFeedDelay = 3;
+
+    /// <summary>
+    /// For mobs that are food, requires killing them before eating.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public bool RequireDead = true;
 }
index 758d42356e45942c081e43744c85413686037426..51f75a2e198cddf6e057c92506dcf6903320b52c 100644 (file)
@@ -96,7 +96,7 @@ public sealed class FoodSystem : EntitySystem
     public (bool Success, bool Handled) TryFeed(EntityUid user, EntityUid target, EntityUid food, FoodComponent foodComp)
     {
         //Suppresses eating yourself and alive mobs
-        if (food == user || _mobState.IsAlive(food))
+        if (food == user || (_mobState.IsAlive(food) && foodComp.RequireDead))
             return (false, false);
 
         // Target can't be fed or they're already eating
@@ -304,33 +304,38 @@ public sealed class FoodSystem : EntitySystem
             return;
         }
 
+        // don't try to repeat if its being deleted
+        args.Repeat = false;
+        DeleteAndSpawnTrash(component, uid, args.User);
+    }
+
+    public void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityUid user)
+    {
         var ev = new BeforeFullyEatenEvent
         {
-            User = args.User
+            User = user
         };
-        RaiseLocalEvent(uid, ev);
+        RaiseLocalEvent(food, ev);
         if (ev.Cancelled)
             return;
 
         if (string.IsNullOrEmpty(component.Trash))
-            QueueDel(uid);
-        else
-            DeleteAndSpawnTrash(component, uid, args.User);
-    }
+        {
+            QueueDel(food);
+            return;
+        }
 
-    public void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityUid? user = null)
-    {
         //We're empty. Become trash.
         var position = Transform(food).MapPosition;
         var finisher = Spawn(component.Trash, position);
 
         // If the user is holding the item
-        if (user != null && _hands.IsHolding(user.Value, food, out var hand))
+        if (_hands.IsHolding(user, food, out var hand))
         {
             Del(food);
 
             // Put the trash in the user's hand
-            _hands.TryPickup(user.Value, finisher, hand);
+            _hands.TryPickup(user, finisher, hand);
             return;
         }
 
@@ -347,7 +352,7 @@ public sealed class FoodSystem : EntitySystem
             return;
 
         // have to kill mouse before eating it
-        if (_mobState.IsAlive(uid))
+        if (_mobState.IsAlive(uid) && component.RequireDead)
             return;
 
         // only give moths eat verb for clothes since it would just fail otherwise
index 26e7a620188460d0916be47356fbe28aed67febf..5e24f0d30fd57b45c4decf59b54e6cc2636e0a90 100644 (file)
         - MobLayer
   - type: Tag
     tags:
+    # let moths eat wool directly
+    - ClothMade
     - DoorBumpOpener
     - Goat
   - type: Reproductive
         reagents:
         - ReagentId: MilkGoat
           Quantity: 30
+      wool:
+        maxVol: 250
   - type: Udder
     reagentId: MilkGoat
     targetSolution: udder
     quantity: 25
     updateRate: 20
+  - type: Wooly
+  - type: Food
+    solution: wool
+    requiresSpecialDigestion: true
+    # Wooly prevents eating wool deleting the goat so its fine
+    requireDead: false
   - type: Butcherable
     spawned:
     - id: FoodMeat