From a23f3fde68f7eeb7cc85377fb1b8f9ec684501fe Mon Sep 17 00:00:00 2001 From: FoLoKe <36813380+FoLoKe@users.noreply.github.com> Date: Sun, 14 Nov 2021 19:33:16 +0300 Subject: [PATCH] Adds cow milking (#5293) --- Content.Client/Entry/IgnoredComponents.cs | 1 + .../Animals/Components/UdderComponent.cs | 48 ++++++ Content.Server/Animals/Systems/UdderSystem.cs | 154 ++++++++++++++++++ .../en-US/animals/udder/udder-system.ftl | 7 + .../Prototypes/Entities/Mobs/NPCs/animals.yml | 12 ++ 5 files changed, 222 insertions(+) create mode 100644 Content.Server/Animals/Components/UdderComponent.cs create mode 100644 Content.Server/Animals/Systems/UdderSystem.cs create mode 100644 Resources/Locale/en-US/animals/udder/udder-system.ftl diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 6e90b7cf3c..d427442afe 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -310,6 +310,7 @@ namespace Content.Client.Entry "NukeCodePaper", "GhostRadio", "Armor", + "Udder", "PneumaticCannon" }; } diff --git a/Content.Server/Animals/Components/UdderComponent.cs b/Content.Server/Animals/Components/UdderComponent.cs new file mode 100644 index 0000000000..3277129692 --- /dev/null +++ b/Content.Server/Animals/Components/UdderComponent.cs @@ -0,0 +1,48 @@ +using System; +using Content.Server.Animals.Systems; +using Content.Shared.FixedPoint; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; + +namespace Content.Server.Animals.Components +{ + [RegisterComponent, Friend(typeof(UdderSystem))] + internal class UdderComponent : Component + { + public override string Name => "Udder"; + + /// + /// The reagent to produce. + /// + [ViewVariables(VVAccess.ReadOnly)] + [DataField("reagentId")] + public string ReagentId = "Milk"; + + /// + /// The solution to add reagent to. + /// + [ViewVariables(VVAccess.ReadOnly)] + [DataField("targetSolution")] + public string TargetSolutionName = "udder"; + + /// + /// The amount of reagent to be generated on update. + /// + [ViewVariables(VVAccess.ReadOnly)] + [DataField("quantity")] + public FixedPoint2 QuantityPerUpdate = 1; + + /// + /// The time between updates (in seconds). + /// + [ViewVariables(VVAccess.ReadOnly)] + [DataField("updateRate")] + public float UpdateRate = 5; + + public float AccumulatedFrameTime; + + public bool BeingMilked; + } +} diff --git a/Content.Server/Animals/Systems/UdderSystem.cs b/Content.Server/Animals/Systems/UdderSystem.cs new file mode 100644 index 0000000000..0d4e362765 --- /dev/null +++ b/Content.Server/Animals/Systems/UdderSystem.cs @@ -0,0 +1,154 @@ +using System; +using Content.Server.Animals.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Content.Server.Chemistry.EntitySystems; +using Content.Server.Nutrition.Components; +using Content.Shared.Nutrition.Components; +using Content.Server.Chemistry.Components.SolutionManager; +using Content.Server.DoAfter; +using Robust.Shared.Localization; +using Content.Shared.Verbs; +using Robust.Shared.Player; +using Content.Server.Popups; + +namespace Content.Server.Animals.Systems +{ + /// + /// Gives ability to living beings with acceptable hunger level to produce milkable reagents. + /// + internal class UdderSystem : EntitySystem + { + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(AddMilkVerb); + SubscribeLocalEvent(OnMilkingFinished); + SubscribeLocalEvent(OnMilkingFailed); + } + + public override void Update(float frameTime) + { + foreach (var udder in EntityManager.EntityQuery(false)) + { + udder.AccumulatedFrameTime += frameTime; + + if (udder.AccumulatedFrameTime < udder.UpdateRate) + continue; + + // Actually there is food digestion so no problem with instant reagent generation "OnFeed" + if (udder.Owner.TryGetComponent(out var hunger)) + { + hunger.HungerThresholds.TryGetValue(HungerThreshold.Peckish, out var targetThreshold); + + // Is there enough nutrition to produce reagent? + if (hunger.CurrentHunger < targetThreshold) + continue; + } + + if (!_solutionContainerSystem.TryGetSolution(udder.OwnerUid, udder.TargetSolutionName, out var solution)) + continue; + + //TODO: toxins from bloodstream !? + _solutionContainerSystem.TryAddReagent(udder.OwnerUid, solution, udder.ReagentId, udder.QuantityPerUpdate, out var accepted); + udder.AccumulatedFrameTime = 0; + } + } + + private void AttemptMilk(EntityUid uid, EntityUid userUid, EntityUid containerUid, UdderComponent? udder = null) + { + if (!Resolve(uid, ref udder)) + return; + + if (udder.BeingMilked) + { + _popupSystem.PopupEntity(Loc.GetString("udder-system-already-milking"), uid, Filter.Entities(userUid)); + return; + } + + udder.BeingMilked = true; + + var doargs = new DoAfterEventArgs(userUid, 5, default, uid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnStun = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + TargetFinishedEvent = new MilkingFinishedEvent(userUid, containerUid), + TargetCancelledEvent = new MilkingFailEvent() + }; + + _doAfterSystem.DoAfter(doargs); + } + + private void OnMilkingFinished(EntityUid uid, UdderComponent udder, MilkingFinishedEvent ev) + { + udder.BeingMilked = false; + + if (!_solutionContainerSystem.TryGetSolution(uid, udder.TargetSolutionName, out var solution)) + return; + + if (!_solutionContainerSystem.TryGetRefillableSolution(ev.ContainerUid, out var targetSolution)) + return; + + var quantity = solution.TotalVolume; + if(quantity == 0) + { + _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, Filter.Entities(ev.UserUid)); + return; + } + + if (quantity > targetSolution.AvailableVolume) + quantity = targetSolution.AvailableVolume; + + var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity); + _solutionContainerSystem.TryAddSolution(ev.ContainerUid, targetSolution, split); + + var container = EntityManager.GetEntity(ev.ContainerUid); + _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", container)), uid, Filter.Entities(ev.UserUid)); + } + + private void OnMilkingFailed(EntityUid uid, UdderComponent component, MilkingFailEvent ev) + { + component.BeingMilked = false; + } + + private void AddMilkVerb(EntityUid uid, UdderComponent component, GetAlternativeVerbsEvent args) + { + if (args.Using == null || + !args.CanInteract || + !args.Using.HasComponent()) + return; + + Verb verb = new(); + verb.Act = () => + { + AttemptMilk(uid, args.User.Uid, args.Using.Uid, component); + }; + verb.Text = Loc.GetString("udder-system-verb-milk"); + verb.Priority = 2; + args.Verbs.Add(verb); + } + + private class MilkingFinishedEvent : EntityEventArgs + { + public EntityUid UserUid; + public EntityUid ContainerUid; + + public MilkingFinishedEvent(EntityUid userUid, EntityUid containerUid) + { + UserUid = userUid; + ContainerUid = containerUid; + } + } + + private class MilkingFailEvent : EntityEventArgs + { } + } +} diff --git a/Resources/Locale/en-US/animals/udder/udder-system.ftl b/Resources/Locale/en-US/animals/udder/udder-system.ftl new file mode 100644 index 0000000000..8479ae08bf --- /dev/null +++ b/Resources/Locale/en-US/animals/udder/udder-system.ftl @@ -0,0 +1,7 @@ +### Udder system + +udder-system-already-milking = The udder is already being milked. +udder-system-success = You fill {THE($target)} with {$amount}u from the udder. +udder-system-dry = The udder is dry. + +udder-system-verb-milk = Milk diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 0eb0e2fc30..e42429fa6f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -177,6 +177,18 @@ normal: cow crit: dead dead: dead + - type: SolutionContainerManager + solutions: + udder: + maxVol: 250 + reagents: + - ReagentId: Milk + Quantity: 30 + - type: Udder + reagentId: Milk + targetSolution: udder + quantity: 25 + updateRate: 30 - type: entity name: crab