From 9db2fbefe1b8112bb692a6dadb2c67044ebfa5f9 Mon Sep 17 00:00:00 2001 From: FoLoKe <36813380+FoLoKe@users.noreply.github.com> Date: Tue, 30 Nov 2021 13:20:13 +0300 Subject: [PATCH] Moves Sliceable to ECS (#5552) --- .../Components/SliceableFoodComponent.cs | 97 +------------ .../EntitySystems/SliceableFoodSystem.cs | 127 ++++++++++++++++++ .../Objects/Consumable/Food/Baked/bread.yml | 2 +- .../Objects/Consumable/Food/Baked/cake.yml | 6 +- .../Objects/Consumable/Food/Baked/pie.yml | 12 +- .../Objects/Consumable/Food/Baked/pizza.yml | 11 +- .../Objects/Consumable/Food/ingredients.yml | 9 +- 7 files changed, 162 insertions(+), 102 deletions(-) create mode 100644 Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs diff --git a/Content.Server/Nutrition/Components/SliceableFoodComponent.cs b/Content.Server/Nutrition/Components/SliceableFoodComponent.cs index f1b5901fb8..dc7479a780 100644 --- a/Content.Server/Nutrition/Components/SliceableFoodComponent.cs +++ b/Content.Server/Nutrition/Components/SliceableFoodComponent.cs @@ -1,113 +1,30 @@ -using System.Threading.Tasks; -using Content.Server.Chemistry.Components.SolutionManager; -using Content.Server.Chemistry.EntitySystems; -using Content.Server.Hands.Components; -using Content.Server.Items; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Examine; -using Content.Shared.FixedPoint; -using Content.Shared.Interaction; +using Content.Server.Nutrition.EntitySystems; using Content.Shared.Sound; -using Robust.Shared.Audio; -using Robust.Shared.Containers; +using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using Robust.Shared.Player; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.Nutrition.Components { - [RegisterComponent] -#pragma warning disable 618 - class SliceableFoodComponent : Component, IInteractUsing, IExamine -#pragma warning restore 618 + [RegisterComponent, Friend(typeof(SliceableFoodSystem))] + internal class SliceableFoodComponent : Component { public override string Name => "SliceableFood"; - int IInteractUsing.Priority => 1; // take priority over eating with utensils - [DataField("slice")] [ViewVariables(VVAccess.ReadWrite)] - private string _slice = string.Empty; + public string Slice = string.Empty; [DataField("sound")] [ViewVariables(VVAccess.ReadWrite)] - private SoundSpecifier _sound = new SoundPathSpecifier("/Audio/Items/Culinary/chop.ogg"); + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Items/Culinary/chop.ogg"); [DataField("count")] [ViewVariables(VVAccess.ReadWrite)] - private ushort _totalCount = 5; + public ushort TotalCount = 5; [ViewVariables(VVAccess.ReadWrite)] public ushort Count; - - protected override void Initialize() - { - base.Initialize(); - Count = _totalCount; - var foodComp = Owner.EnsureComponent(); - Owner.EnsureComponent(); - EntitySystem.Get().EnsureSolution(Owner.Uid, foodComp.SolutionName); - } - - async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) - { - if (string.IsNullOrEmpty(_slice)) - { - return false; - } - - var scs = EntitySystem.Get(); - - if (!Owner.TryGetComponent(out var foodComp) || !scs.TryGetSolution(Owner.Uid, foodComp.SolutionName, out var solution)) - { - return false; - } - - if (!eventArgs.Using.TryGetComponent(out UtensilComponent? utensil) || (utensil.Types & UtensilType.Knife) == 0) - { - return false; - } - - var itemToSpawn = Owner.EntityManager.SpawnEntity(_slice, Owner.Transform.Coordinates); - // This is done this way so that... food additives (read: poisons) remain in the system. - // Basically, we want to: - // 1. Split off a representative chunk - var lostSolution = scs.SplitSolution(Owner.Uid, solution, - solution.CurrentVolume / FixedPoint2.New(Count)); - // 2. Delete the Nutriment (it's already in the target) so we just have additives - // It might be an idea to remove the removal of Nutriment & clear the food - lostSolution.RemoveReagent("Nutriment", lostSolution.GetReagentQuantity("Nutriment")); - // 3. Dump whatever we can into the slice - if (itemToSpawn.TryGetComponent(out var itsFoodComp) && scs.TryGetSolution(itemToSpawn.Uid, itsFoodComp.SolutionName, out var itsSolution)) - { - var lostSolutionPart = lostSolution.SplitSolution(itsSolution.AvailableVolume); - scs.TryAddSolution(itemToSpawn.Uid, itsSolution, lostSolutionPart); - } - if (eventArgs.User.TryGetComponent(out HandsComponent? handsComponent)) - { - if (ContainerHelpers.IsInContainer(Owner)) - { - handsComponent.PutInHandOrDrop(itemToSpawn.GetComponent()); - } - } - - SoundSystem.Play(Filter.Pvs(Owner), _sound.GetSound(), Owner.Transform.Coordinates, - AudioParams.Default.WithVolume(-2)); - - Count--; - if (Count < 1) - { - Owner.Delete(); - } - return true; - } - - public void Examine(FormattedMessage message, bool inDetailsRange) - { - message.AddMarkup(Loc.GetString("sliceable-food-component-on-examine-remaining-slices-text", ("remainingCount", Count))); - } } } diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs new file mode 100644 index 0000000000..396101dbdf --- /dev/null +++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs @@ -0,0 +1,127 @@ +using Content.Server.Chemistry.Components.SolutionManager; +using Content.Server.Chemistry.EntitySystems; +using Content.Server.Hands.Components; +using Content.Server.Items; +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Examine; +using Content.Shared.FixedPoint; +using Content.Shared.Interaction; +using Robust.Shared.Audio; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Player; + +namespace Content.Server.Nutrition.EntitySystems +{ + internal class SliceableFoodSystem : EntitySystem + { + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnComponentStartup); + } + + private void OnInteractUsing(EntityUid uid, SliceableFoodComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + if (TrySliceFood(uid, args.UserUid, args.UsedUid, component)) + args.Handled = true; + } + + private bool TrySliceFood(EntityUid uid, EntityUid user, EntityUid usedItem, + SliceableFoodComponent? component = null, FoodComponent? food = null, TransformComponent? transform = null) + { + if (!Resolve(uid, ref component, ref food, ref transform) || + string.IsNullOrEmpty(component.Slice)) + { + return false; + } + + if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var solution)) + { + return false; + } + + if (!EntityManager.TryGetComponent(usedItem, out UtensilComponent ? utensil) || (utensil.Types & UtensilType.Knife) == 0) + { + return false; + } + + var sliceUid = EntityManager.SpawnEntity(component.Slice, transform.Coordinates).Uid; + + var lostSolution = _solutionContainerSystem.SplitSolution(uid, solution, + solution.CurrentVolume / FixedPoint2.New(component.Count)); + + // Fill new slice + FillSlice(sliceUid, lostSolution); + + if (EntityManager.TryGetComponent(user, out HandsComponent? handsComponent)) + { + if (ContainerHelpers.IsInContainer(component.Owner)) + { + handsComponent.PutInHandOrDrop(EntityManager.GetComponent(sliceUid)); + } + } + + SoundSystem.Play(Filter.Pvs(uid), component.Sound.GetSound(), transform.Coordinates, + AudioParams.Default.WithVolume(-2)); + + component.Count--; + // If someone makes food proto with 1 slice... + if (component.Count < 1) + { + EntityManager.DeleteEntity(uid); + return true; + } + + // Split last slice + if (component.Count == 1) { + var lastSlice = EntityManager.SpawnEntity(component.Slice, transform.Coordinates).Uid; + + // Fill last slice with the rest of the solution + FillSlice(lastSlice, solution); + + EntityManager.DeleteEntity(uid); + } + + return true; + } + + private void FillSlice(EntityUid sliceUid, Solution solution) + { + // Replace all reagents on prototype not just copying poisons (example: slices of eaten pizza should have less nutrition) + if (EntityManager.TryGetComponent(sliceUid, out var sliceFoodComp) && + _solutionContainerSystem.TryGetSolution(sliceUid, sliceFoodComp.SolutionName, out var itsSolution)) + { + _solutionContainerSystem.RemoveAllSolution(sliceUid, itsSolution); + + var lostSolutionPart = solution.SplitSolution(itsSolution.AvailableVolume); + _solutionContainerSystem.TryAddSolution(sliceUid, itsSolution, lostSolutionPart); + } + } + + private void OnComponentStartup(EntityUid uid, SliceableFoodComponent component, ComponentStartup args) + { + component.Count = component.TotalCount; + var foodComp = EntityManager.EnsureComponent(uid); + + EntityManager.EnsureComponent(uid); + _solutionContainerSystem.EnsureSolution(uid, foodComp.SolutionName); + } + + private void OnExamined(EntityUid uid, SliceableFoodComponent component, ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("sliceable-food-component-on-examine-remaining-slices-text", ("remainingCount", component.Count))); + } + } +} diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml index 079bd0e086..9cde0d8139 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml @@ -28,7 +28,7 @@ maxVol: 8 reagents: - ReagentId: Nutriment - Quantity: 7 + Quantity: 4 # Custom Bread Example diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml index 2c04c198c3..dbefa70d7f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml @@ -32,7 +32,7 @@ maxVol: 8 reagents: - ReagentId: Nutriment - Quantity: 7 + Quantity: 4 # Custom Cake Example @@ -279,10 +279,10 @@ - type: SolutionContainerManager solutions: food: - maxVol: 10 # There is something weird about SliceBase: SliceableFoodComp has 5 default slices, which leads to total volume of 40, but CakeBase has only 26 total volume + maxVol: 8 reagents: - ReagentId: Nutriment - Quantity: 7 # TODO: Recalculate volumes and nutrition for sliceable food + Quantity: 4 - ReagentId: Theobromine Quantity: 1 # Tastes like sweetness, cake, chocolate. diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml index 0f778f0aec..94655c4072 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml @@ -13,10 +13,10 @@ - type: SolutionContainerManager solutions: food: - maxVol: 20 + maxVol: 24 reagents: - ReagentId: Nutriment - Quantity: 15 + Quantity: 20 - type: SliceableFood count: 4 - type: Tag @@ -24,13 +24,16 @@ - NoSpinOnThrow - type: entity - parent: FoodPieBase + parent: FoodInjectableBase # Not sliceable id: FoodPieSliceBase abstract: true description: A slice of pie. Tasty! components: - type: Food trash: FoodPlateSmall + - type: Sprite + sprite: Objects/Consumable/Food/Baked/pie.rsi + netsync: false - type: SolutionContainerManager solutions: food: @@ -38,6 +41,9 @@ reagents: - ReagentId: Nutriment Quantity: 5 + - type: Tag + tags: + - NoSpinOnThrow # Pie diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml index 836cde22ff..2440c4d435 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml @@ -13,12 +13,12 @@ - type: SolutionContainerManager solutions: food: - maxVol: 20 + maxVol: 36 reagents: - ReagentId: Nutriment - Quantity: 15 + Quantity: 30 - type: SliceableFood - count: 8 + count: 6 - type: Item size: 8 - type: Tag @@ -26,12 +26,15 @@ - Pizza - type: entity - parent: FoodPizzaBase + parent: FoodInjectableBase # Not sliceable id: FoodPizzaSliceBase abstract: true components: - type: Food trash: FoodPlateSmall + - type: Sprite + sprite: Objects/Consumable/Food/Baked/pizza.rsi + netsync: false - type: SolutionContainerManager solutions: food: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml index 0d56bc2b42..9438deb75d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml @@ -314,7 +314,7 @@ - type: SolutionContainerManager solutions: food: - maxVol: 15 + maxVol: 18 reagents: - ReagentId: Nutriment Quantity: 15 @@ -433,6 +433,13 @@ components: - type: Sprite state: cheesewedge + - type: SolutionContainerManager + solutions: + food: + maxVol: 6 + reagents: + - ReagentId: Nutriment + Quantity: 5 - type: entity name: burned mess