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