UdderSystem.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using Content.Shared.Chemistry.Components;
  2. using Content.Shared.Chemistry.EntitySystems;
  3. using Content.Shared.DoAfter;
  4. using Content.Shared.IdentityManagement;
  5. using Content.Shared.Mobs.Systems;
  6. using Content.Shared.Nutrition.Components;
  7. using Content.Shared.Nutrition.EntitySystems;
  8. using Content.Shared.Popups;
  9. using Content.Shared.Udder;
  10. using Content.Shared.Verbs;
  11. using Robust.Shared.Timing;
  12. namespace Content.Shared.Animals;
  13. /// <summary>
  14. /// Gives the ability to produce milkable reagents;
  15. /// produces endlessly if the owner does not have a HungerComponent.
  16. /// </summary>
  17. public sealed class UdderSystem : EntitySystem
  18. {
  19. [Dependency] private readonly HungerSystem _hunger = default!;
  20. [Dependency] private readonly IGameTiming _timing = default!;
  21. [Dependency] private readonly MobStateSystem _mobState = default!;
  22. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  23. [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
  24. [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
  25. public override void Initialize()
  26. {
  27. base.Initialize();
  28. SubscribeLocalEvent<UdderComponent, MapInitEvent>(OnMapInit);
  29. SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
  30. SubscribeLocalEvent<UdderComponent, MilkingDoAfterEvent>(OnDoAfter);
  31. }
  32. private void OnMapInit(EntityUid uid, UdderComponent component, MapInitEvent args)
  33. {
  34. component.NextGrowth = _timing.CurTime + component.GrowthDelay;
  35. }
  36. public override void Update(float frameTime)
  37. {
  38. base.Update(frameTime);
  39. var query = EntityQueryEnumerator<UdderComponent>();
  40. while (query.MoveNext(out var uid, out var udder))
  41. {
  42. if (_timing.CurTime < udder.NextGrowth)
  43. continue;
  44. udder.NextGrowth += udder.GrowthDelay;
  45. if (_mobState.IsDead(uid))
  46. continue;
  47. if (!_solutionContainerSystem.ResolveSolution(uid, udder.SolutionName, ref udder.Solution, out var solution))
  48. continue;
  49. if (solution.AvailableVolume == 0)
  50. continue;
  51. // Actually there is food digestion so no problem with instant reagent generation "OnFeed"
  52. if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
  53. {
  54. // Is there enough nutrition to produce reagent?
  55. if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)
  56. continue;
  57. _hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
  58. }
  59. //TODO: toxins from bloodstream !?
  60. _solutionContainerSystem.TryAddReagent(udder.Solution.Value, udder.ReagentId, udder.QuantityPerUpdate, out _);
  61. }
  62. }
  63. private void AttemptMilk(Entity<UdderComponent?> udder, EntityUid userUid, EntityUid containerUid)
  64. {
  65. if (!Resolve(udder, ref udder.Comp))
  66. return;
  67. var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), udder, udder, used: containerUid)
  68. {
  69. BreakOnMove = true,
  70. BreakOnDamage = true,
  71. MovementThreshold = 1.0f,
  72. };
  73. _doAfterSystem.TryStartDoAfter(doargs);
  74. }
  75. private void OnDoAfter(Entity<UdderComponent> entity, ref MilkingDoAfterEvent args)
  76. {
  77. if (args.Cancelled || args.Handled || args.Args.Used == null)
  78. return;
  79. if (!_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
  80. return;
  81. if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution))
  82. return;
  83. args.Handled = true;
  84. var quantity = solution.Volume;
  85. if (quantity == 0)
  86. {
  87. _popupSystem.PopupClient(Loc.GetString("udder-system-dry"), entity.Owner, args.Args.User);
  88. return;
  89. }
  90. if (quantity > targetSolution.AvailableVolume)
  91. quantity = targetSolution.AvailableVolume;
  92. var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
  93. _solutionContainerSystem.TryAddSolution(targetSoln.Value, split);
  94. _popupSystem.PopupClient(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner,
  95. args.Args.User, PopupType.Medium);
  96. }
  97. private void AddMilkVerb(Entity<UdderComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
  98. {
  99. if (args.Using == null ||
  100. !args.CanInteract ||
  101. !EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
  102. return;
  103. var uid = entity.Owner;
  104. var user = args.User;
  105. var used = args.Using.Value;
  106. AlternativeVerb verb = new()
  107. {
  108. Act = () =>
  109. {
  110. AttemptMilk(uid, user, used);
  111. },
  112. Text = Loc.GetString("udder-system-verb-milk"),
  113. Priority = 2
  114. };
  115. args.Verbs.Add(verb);
  116. }
  117. }