From 1ba6cad88084ca3ff368c5fd42985dccf30058ad Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Fri, 17 Nov 2023 08:51:51 +0000 Subject: [PATCH] moths can eat goat wool (#21704) * 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> --- .../Animals/Components/WoolyComponent.cs | 42 ++++++++++++++ Content.Server/Animals/Systems/WoolySystem.cs | 56 +++++++++++++++++++ .../Nutrition/Components/FoodComponent.cs | 6 ++ .../Nutrition/EntitySystems/FoodSystem.cs | 29 ++++++---- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 10 ++++ 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 Content.Server/Animals/Components/WoolyComponent.cs create mode 100644 Content.Server/Animals/Systems/WoolySystem.cs diff --git a/Content.Server/Animals/Components/WoolyComponent.cs b/Content.Server/Animals/Components/WoolyComponent.cs new file mode 100644 index 0000000000..8db44973e5 --- /dev/null +++ b/Content.Server/Animals/Components/WoolyComponent.cs @@ -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; + +/// +/// Lets an animal grow a wool solution when not hungry. +/// +[RegisterComponent, Access(typeof(WoolySystem))] +public sealed partial class WoolyComponent : Component +{ + /// + /// What reagent to grow. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public ProtoId ReagentId = "Fiber"; + + /// + /// How much wool to grow at every growth cycle. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 Quantity = 25; + + /// + /// What solution to add the wool reagent to. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public string Solution = "wool"; + + /// + /// How long to wait before growing wool. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1); + + /// + /// When to next try growing wool. + /// + [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 index 0000000000..e63718c395 --- /dev/null +++ b/Content.Server/Animals/Systems/WoolySystem.cs @@ -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; + +/// +/// 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. +/// +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(OnBeforeFullyEaten); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + 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 ent, ref BeforeFullyEatenEvent args) + { + // don't want moths to delete goats after eating them + args.Cancel(); + } +} diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index af74352132..c7f90ccff1 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -67,4 +67,10 @@ public sealed partial class FoodComponent : Component /// [DataField] public float ForceFeedDelay = 3; + + /// + /// For mobs that are food, requires killing them before eating. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool RequireDead = true; } diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 758d42356e..51f75a2e19 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -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 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 26e7a62018..5e24f0d30f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -763,6 +763,8 @@ - MobLayer - type: Tag tags: + # let moths eat wool directly + - ClothMade - DoorBumpOpener - Goat - type: Reproductive @@ -788,11 +790,19 @@ 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 -- 2.51.2