Add kitchen knife & sliceable foods (#2891)

* sliceable food, kitchen knives

* sprite fixes, sounds

* add to vendor

* address reviews

* address reviews

* stereo -> mono

* fix wrong amount of nutriment being removed

* oops im dumb

* meta.json

* fix merge

* probably fix test

* remove all instances of [ComponentReference(typeof(IAfterInteract))]

Co-authored-by: cyclowns <cyclowns@protonmail.ch>
This commit is contained in:
mirrorcult
2021-01-06 18:48:08 -07:00
committed by GitHub
parent a7c4571075
commit 7977992e5a
63 changed files with 597 additions and 301 deletions

View File

@@ -0,0 +1,92 @@
using System.Threading.Tasks;
using Content.Shared.Chemistry;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.GameObjects.EntitySystems;
using Content.Server.GameObjects.Components.Nutrition;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.Serialization;
using Robust.Shared.Localization;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
using Robust.Shared.Audio;
namespace Content.Server.GameObjects.Components.Culinary
{
[RegisterComponent]
class SliceableFoodComponent : Component, IInteractUsing, IExamine
{
public override string Name => "SliceableFood";
int IInteractUsing.Priority => 1; // take priority over eating with utensils
[ViewVariables(VVAccess.ReadWrite)] private string _slice;
private ushort _totalCount;
[ViewVariables(VVAccess.ReadWrite)] private string _sound;
[ViewVariables(VVAccess.ReadWrite)] public ushort Count;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _slice, "slice", string.Empty);
serializer.DataField(ref _sound, "sound", "/Audio/Items/Culinary/chop.ogg");
serializer.DataField<ushort>(ref _totalCount, "count", 5);
}
public override void Initialize()
{
base.Initialize();
Count = _totalCount;
Owner.EnsureComponent<FoodComponent>();
Owner.EnsureComponent<SolutionContainerComponent>();
}
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
if (string.IsNullOrEmpty(_slice))
{
return false;
}
if (!Owner.TryGetComponent(out SolutionContainerComponent solution))
{
return false;
}
if (!eventArgs.Using.TryGetComponent(out UtensilComponent utensil) || !utensil.HasType(UtensilType.Knife))
{
return false;
}
var itemToSpawn = Owner.EntityManager.SpawnEntity(_slice, Owner.Transform.Coordinates);
if (eventArgs.User.TryGetComponent(out HandsComponent handsComponent))
{
if (ContainerHelpers.IsInContainer(Owner))
{
handsComponent.PutInHandOrDrop(itemToSpawn.GetComponent<ItemComponent>());
}
}
EntitySystem.Get<AudioSystem>().PlayAtCoords(_sound, Owner.Transform.Coordinates,
AudioParams.Default.WithVolume(-2));
Count--;
if (Count < 1)
{
Owner.Delete();
return true;
}
solution.TryRemoveReagent("chem.Nutriment", solution.CurrentVolume / ReagentUnit.New(Count + 1));
return true;
}
public void Examine(FormattedMessage message, bool inDetailsRange)
{
message.AddMarkup(Loc.GetString($"There are { Count } slices remaining."));
}
}
}

View File

@@ -0,0 +1,139 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Nutrition;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Culinary
{
[RegisterComponent]
public class UtensilComponent : Component, IAfterInteract
{
public override string Name => "Utensil";
private UtensilType _types = UtensilType.None;
[ViewVariables]
public UtensilType Types
{
get => _types;
set
{
if (_types.Equals(value))
return;
_types = value;
}
}
/// <summary>
/// The chance that the utensil has to break with each use.
/// A value of 0 means that it is unbreakable.
/// </summary>
[ViewVariables]
private float _breakChance;
/// <summary>
/// The sound to be played if the utensil breaks.
/// </summary>
[ViewVariables]
private string? _breakSound;
public void AddType(UtensilType type)
{
Types |= type;
}
public bool HasAnyType(UtensilType type)
{
return (_types & type) != UtensilType.None;
}
public bool HasType(UtensilType type)
{
return (_types & type) != 0;
}
public void RemoveType(UtensilType type)
{
Types &= ~type;
}
internal void TryBreak(IEntity user)
{
if (_breakSound != null && IoCManager.Resolve<IRobustRandom>().Prob(_breakChance))
{
EntitySystem.Get<AudioSystem>()
.PlayFromEntity(_breakSound, user, AudioParams.Default.WithVolume(-2f));
Owner.Delete();
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction("types",
new List<UtensilType>(),
types => types.ForEach(AddType),
() =>
{
var types = new List<UtensilType>();
foreach (UtensilType type in Enum.GetValues(typeof(UtensilType)))
{
if ((Types & type) != 0)
{
types.Add(type);
}
}
return types;
});
serializer.DataField(ref _breakChance, "breakChance", 0);
serializer.DataField(ref _breakSound, "breakSound", "/Audio/Items/snap.ogg");
}
async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
TryUseUtensil(eventArgs.User, eventArgs.Target);
}
private void TryUseUtensil(IEntity user, IEntity target)
{
if (!target.TryGetComponent(out FoodComponent? food))
{
return;
}
if (!user.InRangeUnobstructed(target, popup: true))
{
return;
}
food.TryUseFood(user, null, this);
}
}
[Flags]
public enum UtensilType : byte
{
None = 0,
Fork = 1,
Spoon = 1 << 1,
Knife = 1 << 2
}
}