Files
crystall-punk-14/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs

239 lines
7.6 KiB
C#
Raw Normal View History

#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
Async Interface IAfterInteract() (#2735) * Async Interface * Update Content.Server/GameObjects/Components/Fluids/MopComponent.cs Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Changed the glassbeaker * Update Content.Shared/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Content.Shared/Interfaces/GameObjects/Components/Interaction/IAfterInteract.cs Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Interaction system fix * Removed I from the interface * Changed all implementations of the interface I could find * all public void implementation fixed * All built, no errors should remain * Update Resources/Prototypes/Entities/Objects/Specific/chemistry.yml Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/GameObjects/Components/Portal/TeleporterComponent.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Commit based off Sloth's commentary * Removed the Rag file from the PR * Reverted sloth's commentary changes on the publcity of the function * Injector component properly implemented interface * Update Content.Server/GameObjects/Components/Fluids/MopComponent.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/GameObjects/Components/Fluids/SprayComponent.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: BlueberryShortcake <rubetskoy234@mail.ru> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2020-12-17 10:45:04 +03:00
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Body.Behavior;
using Content.Server.GameObjects.Components.Chemistry;
Add changing the amount of hands on the GUI depending on your body parts (#1406) * Multiple hands in gui first pass * Remove IHandsComponent interface * Create hand class and more hand textures * Refactor ServerHandsComponent to use a single list of hands * Seal SharedHand * Fix picked up items not showing on top of the hand buttons * Remove HandsGui buttons and panels dictionaries * Fix items in hands rendering * Fix wrong hand container comparison * Fix not updating the location of duplicate hands * Change ClientHandsComponent to use a SortedList instead of a dictionary * More merge conflict fixes * Change SortedList to List * Fix hand button order * Add item tooltip for more than 2 hands and updating when removing hands * Add add hand and remove hand command * Merge conflict fixes * Remove nullable reference type from ContainerSlot * Fix texture errors * Fix error when reaching 0 hands * Fix error when swapping hands with no hands * Merged remove hand methods * Fix item panel texture errors * Merge conflict fixes * Fix addhand and removehand command descriptions * Add properly displaying tooltips for 2 hands * Make hand indexes and locations consistent across the client and server * Add dropping held entity if a hand is removed * Change hand location to be calculated by index * Made different hand gui updates more consistent * Remove human body yml testing changes * Sanitize addhand and removehand commands * Merge conflict fixes * Remove testing changes * Revert body system changes * Add missing imports * Remove obsolete hands parameter in yml files * Fix broken import * Fix startup error and adding and removing hands on the same tick * Make hand container id use an uint In case someone gets more than 2 billion hands * Rename hand component files * Make hands state use an array
2020-07-25 15:11:16 +02:00
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Culinary;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Nutrition
{
[RegisterComponent]
public class FoodComponent : Component, IUse, IAfterInteract
{
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
public override string Name => "Food";
[ViewVariables] private string _useSound = "";
[ViewVariables] private string? _trashPrototype;
[ViewVariables] private ReagentUnit _transferAmount;
private UtensilType _utensilsNeeded;
[ViewVariables]
public int UsesRemaining
{
get
{
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
{
return 0;
}
return solution.CurrentVolume == 0
? 0
: Math.Max(1, (int)Math.Ceiling((solution.CurrentVolume / _transferAmount).Float()));
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _useSound, "useSound", "/Audio/Items/eatfood.ogg");
serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5));
serializer.DataField(ref _trashPrototype, "trash", null);
serializer.DataReadWriteFunction(
"utensils",
new List<UtensilType>(),
types => types.ForEach(type => _utensilsNeeded |= type),
() =>
{
var types = new List<UtensilType>();
foreach (var type in (UtensilType[]) Enum.GetValues(typeof(UtensilType)))
{
if ((_utensilsNeeded & type) != 0)
{
types.Add(type);
}
}
return types;
});
}
public override void Initialize()
{
base.Initialize();
Owner.EnsureComponentWarn<SolutionContainerComponent>();
}
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{
if (_utensilsNeeded != UtensilType.None)
{
eventArgs.User.PopupMessage(Loc.GetString("food-you-need-utensil", ("utensil", _utensilsNeeded)));
return false;
}
return TryUseFood(eventArgs.User, null);
}
// Feeding someone else
A big hecking chemistry-related refactor. (#3055) * A big hecking chemistry-related refactor. Changed SolutionContainerCaps. It now describes "stock" behavior for interacting with solutions that is pre-implemented by SolutionContainerComponent. As such things like syringes do not check it anymore (on themselves) to see "can we remove reagent from ourselves". That's assumed by it... being a syringe. SolutionContainerCaps now has different flags more accurately describing possible reagent interaction behaviors. ISolutionInteractionsComponent is the interface that describes the common behaviors like "what happens when injected with a syringe". This is implemented by SolutionContainerComponent but could be implemented by other classes. One notable example that drove me to making this interface was the /vg/station circuit imprinter which splits reagent poured in into its two reservoir beakers. Having this interface allows us to do this "proxying" behavior hack-free. (the hacks in /vg/ code were somewhat dirty...). PourableComponent has been replaced SolutionTransferComponent. It now describes both give-and-take behavior for the common reagent containers. This is in line with /vg/'s /obj/item/weapon/reagent_containers architecture. "Taking" in this context is ONLY from reagent tanks like fuel tanks. Oh, should I mention that fuel tanks and such have a proper component now? They do. Because of this behavioral change, reagent tanks DO NOT have Pourable anymore. Removing from reagent tanks is now in the hands of the item used on them. Welders and fire extinguishers now have code for removing from them. This sounds bad at first but remember that all have quite unique behavior related to this: Welders cause explosions if lit and can ONLY be fueled at fuel tanks. Extinguishers can be filled at any tank, etc... The code for this is also simpler due to ISolutionInteractionsComponent now so... IAfterInteract now works like IInteractUsing with the Priority levels and "return true to block further handlers" behavior. This was necessary to make extinguishers prioritize taking from tanks over spraying. Explicitly coded interactions like welders refueling also means they refuse instantly to full now, which they didn't before. And it plays the sound. Etc... Probably more stuff I'm forgetting. * Review improvements.
2021-02-03 14:05:31 +01:00
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
if (eventArgs.Target == null)
{
A big hecking chemistry-related refactor. (#3055) * A big hecking chemistry-related refactor. Changed SolutionContainerCaps. It now describes "stock" behavior for interacting with solutions that is pre-implemented by SolutionContainerComponent. As such things like syringes do not check it anymore (on themselves) to see "can we remove reagent from ourselves". That's assumed by it... being a syringe. SolutionContainerCaps now has different flags more accurately describing possible reagent interaction behaviors. ISolutionInteractionsComponent is the interface that describes the common behaviors like "what happens when injected with a syringe". This is implemented by SolutionContainerComponent but could be implemented by other classes. One notable example that drove me to making this interface was the /vg/station circuit imprinter which splits reagent poured in into its two reservoir beakers. Having this interface allows us to do this "proxying" behavior hack-free. (the hacks in /vg/ code were somewhat dirty...). PourableComponent has been replaced SolutionTransferComponent. It now describes both give-and-take behavior for the common reagent containers. This is in line with /vg/'s /obj/item/weapon/reagent_containers architecture. "Taking" in this context is ONLY from reagent tanks like fuel tanks. Oh, should I mention that fuel tanks and such have a proper component now? They do. Because of this behavioral change, reagent tanks DO NOT have Pourable anymore. Removing from reagent tanks is now in the hands of the item used on them. Welders and fire extinguishers now have code for removing from them. This sounds bad at first but remember that all have quite unique behavior related to this: Welders cause explosions if lit and can ONLY be fueled at fuel tanks. Extinguishers can be filled at any tank, etc... The code for this is also simpler due to ISolutionInteractionsComponent now so... IAfterInteract now works like IInteractUsing with the Priority levels and "return true to block further handlers" behavior. This was necessary to make extinguishers prioritize taking from tanks over spraying. Explicitly coded interactions like welders refueling also means they refuse instantly to full now, which they didn't before. And it plays the sound. Etc... Probably more stuff I'm forgetting. * Review improvements.
2021-02-03 14:05:31 +01:00
return false;
}
TryUseFood(eventArgs.User, eventArgs.Target);
A big hecking chemistry-related refactor. (#3055) * A big hecking chemistry-related refactor. Changed SolutionContainerCaps. It now describes "stock" behavior for interacting with solutions that is pre-implemented by SolutionContainerComponent. As such things like syringes do not check it anymore (on themselves) to see "can we remove reagent from ourselves". That's assumed by it... being a syringe. SolutionContainerCaps now has different flags more accurately describing possible reagent interaction behaviors. ISolutionInteractionsComponent is the interface that describes the common behaviors like "what happens when injected with a syringe". This is implemented by SolutionContainerComponent but could be implemented by other classes. One notable example that drove me to making this interface was the /vg/station circuit imprinter which splits reagent poured in into its two reservoir beakers. Having this interface allows us to do this "proxying" behavior hack-free. (the hacks in /vg/ code were somewhat dirty...). PourableComponent has been replaced SolutionTransferComponent. It now describes both give-and-take behavior for the common reagent containers. This is in line with /vg/'s /obj/item/weapon/reagent_containers architecture. "Taking" in this context is ONLY from reagent tanks like fuel tanks. Oh, should I mention that fuel tanks and such have a proper component now? They do. Because of this behavioral change, reagent tanks DO NOT have Pourable anymore. Removing from reagent tanks is now in the hands of the item used on them. Welders and fire extinguishers now have code for removing from them. This sounds bad at first but remember that all have quite unique behavior related to this: Welders cause explosions if lit and can ONLY be fueled at fuel tanks. Extinguishers can be filled at any tank, etc... The code for this is also simpler due to ISolutionInteractionsComponent now so... IAfterInteract now works like IInteractUsing with the Priority levels and "return true to block further handlers" behavior. This was necessary to make extinguishers prioritize taking from tanks over spraying. Explicitly coded interactions like welders refueling also means they refuse instantly to full now, which they didn't before. And it plays the sound. Etc... Probably more stuff I'm forgetting. * Review improvements.
2021-02-03 14:05:31 +01:00
return true;
}
public virtual bool TryUseFood(IEntity? user, IEntity? target, UtensilComponent? utensilUsed = null)
{
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
{
return false;
}
if (user == null)
{
return false;
}
if (UsesRemaining <= 0)
{
user.PopupMessage(Loc.GetString("{0:TheName} is empty!", Owner));
return false;
}
var trueTarget = target ?? user;
if (!trueTarget.TryGetComponent(out IBody? body) ||
!body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
{
return false;
}
var utensils = utensilUsed != null
? new List<UtensilComponent> {utensilUsed}
: null;
if (_utensilsNeeded != UtensilType.None)
{
utensils = new List<UtensilComponent>();
var types = UtensilType.None;
if (user.TryGetComponent(out HandsComponent? hands))
{
foreach (var item in hands.GetAllHeldItems())
{
if (!item.Owner.TryGetComponent(out UtensilComponent? utensil))
{
continue;
}
utensils.Add(utensil);
types |= utensil.Types;
}
}
if (!types.HasFlag(_utensilsNeeded))
{
trueTarget.PopupMessage(user, Loc.GetString("food-you-need-to-hold-utensil", ("utensil", _utensilsNeeded)));
return false;
}
}
if (!user.InRangeUnobstructed(trueTarget, popup: true))
{
return false;
}
var transferAmount = ReagentUnit.Min(_transferAmount, solution.CurrentVolume);
var split = solution.SplitSolution(transferAmount);
var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(split));
if (firstStomach == null)
{
trueTarget.PopupMessage(user, Loc.GetString("You can't eat any more!"));
return false;
}
// TODO: Account for partial transfer.
split.DoEntityReaction(trueTarget, ReactionMethod.Ingestion);
firstStomach.TryTransferSolution(split);
_entitySystem.GetEntitySystem<AudioSystem>()
.PlayFromEntity(_useSound, trueTarget, AudioParams.Default.WithVolume(-1f));
trueTarget.PopupMessage(user, Loc.GetString("Nom"));
// If utensils were used
if (utensils != null)
{
foreach (var utensil in utensils)
{
utensil.TryBreak(user);
}
}
if (UsesRemaining > 0)
{
return true;
}
if (string.IsNullOrEmpty(_trashPrototype))
{
Owner.Delete();
return true;
}
//We're empty. Become trash.
var position = Owner.Transform.Coordinates;
var finisher = Owner.EntityManager.SpawnEntity(_trashPrototype, position);
// If the user is holding the item
if (user.TryGetComponent(out HandsComponent? handsComponent) &&
handsComponent.IsHolding(Owner))
{
Owner.Delete();
// Put the trash in the user's hand
if (finisher.TryGetComponent(out ItemComponent? item) &&
handsComponent.CanPutInHand(item))
{
handsComponent.PutInHand(item);
}
}
else
{
Owner.Delete();
}
return true;
}
}
}