diff --git a/Content.Server/Body/Components/StomachComponent.cs b/Content.Server/Body/Components/StomachComponent.cs
index fe93468f74e..821f4e7afaf 100644
--- a/Content.Server/Body/Components/StomachComponent.cs
+++ b/Content.Server/Body/Components/StomachComponent.cs
@@ -1,4 +1,4 @@
-using Content.Server.Body.Systems;
+using Content.Server.Body.Systems;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
@@ -64,5 +64,17 @@ public ReagentDelta(ReagentQuantity reagentQuantity)
public void Increment(float delta) => Lifetime += delta;
}
+
+ ///
+ /// Frontier - Used by goblin for fliping the food quility effects
+ ///
+ [DataField]
+ public bool ReverseFoodQuality;
+
+ ///
+ /// Frontier - Allow eating trash
+ ///
+ [DataField]
+ public bool TrashDigestion;
}
}
diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs
index 5ead67a12b2..25e4f3a0c95 100644
--- a/Content.Server/Nutrition/Components/FoodComponent.cs
+++ b/Content.Server/Nutrition/Components/FoodComponent.cs
@@ -7,6 +7,30 @@
namespace Content.Server.Nutrition.Components;
+public enum Quality : byte // Frontier
+{
+ High,
+ Normal,
+ Junk,
+ Nasty,
+ Toxin,
+ Trash,
+ MailOpened,
+ MailClosed
+}
+
+public enum FinalQuality : byte // Frontier
+{
+ High,
+ Normal,
+ Junk,
+ Nasty,
+ Toxin,
+ Trash,
+ MailOpened,
+ MailClosed
+}
+
[RegisterComponent, Access(typeof(FoodSystem))]
public sealed partial class FoodComponent : Component
{
@@ -74,4 +98,15 @@ public sealed partial class FoodComponent : Component
///
[DataField, ViewVariables(VVAccess.ReadWrite)]
public bool RequireDead = true;
+
+ ///
+ /// Frontier - Nasty food, used for goblins to know if they can eat it or not
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField] // Frontier
+ public Quality Quality = Quality.Normal;
+
+ ///
+ /// Frontier - Edited by the system to find the final quility results
+ ///
+ public FinalQuality FinalQuality = FinalQuality.Normal;
}
diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs
index 2b627151339..ced6e5c0656 100644
--- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs
+++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs
@@ -30,6 +30,12 @@
using Robust.Shared.Audio.Systems;
using Robust.Shared.Utility;
using System.Linq;
+using Content.Server.Medical; // Frontier
+using Content.Shared.Speech.EntitySystems; // Frontier
+using Robust.Shared.Random; // Frontier
+using Content.Shared.Jittering; // Frontier
+using Content.Server.Chat.Systems; // Frontier
+using Content.Shared.Tag; // Frontier
namespace Content.Server.Nutrition.EntitySystems;
@@ -54,6 +60,12 @@ public sealed class FoodSystem : EntitySystem
[Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly StomachSystem _stomach = default!;
[Dependency] private readonly UtensilSystem _utensil = default!;
+ [Dependency] private readonly VomitSystem _vomit = default!; // Frontier
+ [Dependency] private readonly SharedStutteringSystem _stuttering = default!; // Frontier
+ [Dependency] protected readonly IRobustRandom RobustRandom = default!; // Frontier
+ [Dependency] private readonly SharedJitteringSystem _jittering = default!; // Frontier
+ [Dependency] private readonly ChatSystem _chat = default!; // Frontier
+ [Dependency] private readonly TagSystem _tag = default!; // Frontier
public const float MaxFeedDistance = 1.0f;
@@ -230,6 +242,7 @@ private void OnDoAfter(Entity entity, ref ConsumeDoAfterEvent arg
// Get the stomach with the highest available solution volume
var highestAvailable = FixedPoint2.Zero;
StomachComponent? stomachToUse = null;
+ var reverseFoodQuality = false; // Frontier
foreach (var (stomach, _) in stomachs)
{
var owner = stomach.Owner;
@@ -244,6 +257,7 @@ private void OnDoAfter(Entity entity, ref ConsumeDoAfterEvent arg
stomachToUse = stomach;
highestAvailable = stomachSol.AvailableVolume;
+ reverseFoodQuality = stomachToUse.ReverseFoodQuality; // Frontier
}
// No stomach so just popup a message that they can't eat.
@@ -257,6 +271,143 @@ private void OnDoAfter(Entity entity, ref ConsumeDoAfterEvent arg
_reaction.DoEntityReaction(args.Target.Value, solution, ReactionMethod.Ingestion);
_stomach.TryTransferSolution(stomachToUse.Owner, split, stomachToUse);
+ /// Frontier - Goblin food system
+ if (entity.Comp.Quality == Quality.High)
+ entity.Comp.FinalQuality = FinalQuality.High;
+ else if (entity.Comp.Quality == Quality.Normal)
+ entity.Comp.FinalQuality = FinalQuality.Normal;
+ else if (entity.Comp.Quality == Quality.Junk)
+ entity.Comp.FinalQuality = FinalQuality.Junk;
+ else if (entity.Comp.Quality == Quality.Nasty)
+ entity.Comp.FinalQuality = FinalQuality.Nasty;
+ else if (entity.Comp.Quality == Quality.Toxin)
+ entity.Comp.FinalQuality = FinalQuality.Toxin;
+ else if (entity.Comp.Quality == Quality.Trash)
+ entity.Comp.FinalQuality = FinalQuality.Trash;
+
+ if (reverseFoodQuality)
+ {
+ if (entity.Comp.Quality == Quality.High)
+ entity.Comp.FinalQuality = FinalQuality.Toxin;
+ else if (entity.Comp.Quality == Quality.Normal)
+ entity.Comp.FinalQuality = FinalQuality.Nasty;
+ else if (entity.Comp.Quality == Quality.Nasty)
+ entity.Comp.FinalQuality = FinalQuality.Normal;
+ else if (entity.Comp.Quality == Quality.Toxin)
+ entity.Comp.FinalQuality = FinalQuality.High;
+ }
+
+ // TODO: Add detection for fried food on nasty to update it to toxin for goblins.
+ // TODO: Add inspect food but only for goblin eyes to see, goblins can tell food quality.
+
+ string[] toxinsRegent = { "Toxin", "CarpoToxin", "Mold", "Amatoxin", "SulfuricAcid" };
+ var speedRegent = "Stimulants";
+ var damagingRegent = "Toxin";
+ var emoteId = "Laugh";
+
+ TryComp(args.Target.Value, out var bloodStream);
+
+ switch (entity.Comp.FinalQuality)
+ {
+ case FinalQuality.High:
+ if (reverseFoodQuality)
+ {
+ if (_solutionContainer.ResolveSolution(stomachToUse.Owner, stomachToUse.BodySolutionName, ref stomachToUse.Solution))
+ {
+ foreach (var reagent in toxinsRegent)
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, reagent, FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, "Flavorol", FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ }
+ if (_solutionContainer.ResolveSolution(args.Target.Value, bloodStream!.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+ _solutionContainer.TryAddReagent(bloodStream.ChemicalSolution.Value, speedRegent, FixedPoint2.New((int) transferAmount / 3), out _); // Add to blood
+ }
+ else
+ {
+
+ }
+ break;
+ case FinalQuality.Normal:
+ if (reverseFoodQuality)
+ {
+ if (_solutionContainer.ResolveSolution(stomachToUse.Owner, stomachToUse.BodySolutionName, ref stomachToUse.Solution))
+ {
+ foreach (var reagent in toxinsRegent)
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, reagent, FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, "Flavorol", FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ }
+ if (_solutionContainer.ResolveSolution(args.Target.Value, bloodStream!.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+ _solutionContainer.TryAddReagent(bloodStream.ChemicalSolution.Value, speedRegent, FixedPoint2.New((int) transferAmount / 5), out _); // Add to blood
+ }
+ else
+ {
+
+ }
+ break;
+ case FinalQuality.Junk:
+ if (reverseFoodQuality)
+ {
+ if (_solutionContainer.ResolveSolution(stomachToUse.Owner, stomachToUse.BodySolutionName, ref stomachToUse.Solution))
+ {
+ foreach (var reagent in toxinsRegent)
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, reagent, FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, "Flavorol", FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ }
+ if (_solutionContainer.ResolveSolution(args.Target.Value, bloodStream!.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+ _solutionContainer.TryAddReagent(bloodStream.ChemicalSolution.Value, speedRegent, FixedPoint2.New((int) transferAmount / 7), out _); // Add to blood
+ }
+ else
+ {
+
+ }
+ break;
+ case FinalQuality.Nasty:
+ if (reverseFoodQuality)
+ {
+ if (_solutionContainer.ResolveSolution(stomachToUse.Owner, stomachToUse.BodySolutionName, ref stomachToUse.Solution))
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, "Flavorol", FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ if (_solutionContainer.ResolveSolution(args.Target.Value, bloodStream!.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+ _solutionContainer.TryAddReagent(bloodStream.ChemicalSolution.Value, damagingRegent, FixedPoint2.New((int) transferAmount / 5), out _); // Add to blood
+ _stuttering.DoStutter(args.Target.Value, TimeSpan.FromSeconds(5), false); // Gives stuttering
+ _jittering.DoJitter(args.Target.Value, TimeSpan.FromSeconds(5), true, 40f, 4f, true, null);
+ }
+ else
+ {
+
+ }
+ break;
+ case FinalQuality.Toxin:
+ if (reverseFoodQuality)
+ {
+ if (_solutionContainer.ResolveSolution(stomachToUse.Owner, stomachToUse.BodySolutionName, ref stomachToUse.Solution))
+ _solutionContainer.RemoveReagent(stomachToUse.Solution.Value, "Flavorol", FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ if (_solutionContainer.ResolveSolution(args.Target.Value, bloodStream!.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+ _solutionContainer.TryAddReagent(bloodStream.ChemicalSolution.Value, damagingRegent, FixedPoint2.New((int) transferAmount / 3), out _); // Add to blood
+ _stuttering.DoStutter(args.Target.Value, TimeSpan.FromSeconds(5), false); // Gives stuttering
+ _jittering.DoJitter(args.Target.Value, TimeSpan.FromSeconds(5), true, 80f, 8f, true, null);
+ _chat.TryEmoteWithoutChat(args.Target.Value, emoteId);
+
+ if (RobustRandom.Prob(.05f)) // 5% to puke
+ _vomit.Vomit(args.Target.Value);
+ }
+ else
+ {
+
+ }
+ break;
+ case FinalQuality.Trash:
+ if (_solutionContainer.ResolveSolution(stomachToUse.Owner, stomachToUse.BodySolutionName, ref stomachToUse.Solution))
+ {
+ foreach (var reagent in toxinsRegent)
+ _solutionContainer.RemoveReagent(stomachToUse!.Solution.Value, reagent, FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ _solutionContainer.RemoveReagent(stomachToUse!.Solution.Value, "Flavorol", FixedPoint2.New((int) transferAmount)); // Remove from body before it goes to blood
+ }
+ if (_solutionContainer.ResolveSolution(args.Target.Value, bloodStream!.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+ _solutionContainer.TryAddReagent(bloodStream.ChemicalSolution.Value, speedRegent, FixedPoint2.New((int) transferAmount), out _); // Add to blood
+ break;
+ default:
+ throw new ArgumentOutOfRangeException($"No implemented mask radio behavior for {entity.Comp.Quality}!");
+ } /// Frontier
+
var flavors = args.FlavorMessage;
if (forceFeed)
@@ -402,6 +553,11 @@ private bool IsDigestibleBy(EntityUid food, FoodComponent component, List<(Stoma
// Run through the mobs' stomachs
foreach (var (comp, _) in stomachs)
{
+ // Frontier - Allow trash eating
+ if (!comp.TrashDigestion && component.Quality == Quality.Trash)
+ return false;
+ // Frontier - Allow trash eating
+
// Find a stomach with a SpecialDigestible
if (comp.SpecialDigestible == null)
continue;
diff --git a/Resources/Locale/en-US/_NF/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/_NF/flavors/flavor-profiles.ftl
new file mode 100644
index 00000000000..24494b508e3
--- /dev/null
+++ b/Resources/Locale/en-US/_NF/flavors/flavor-profiles.ftl
@@ -0,0 +1 @@
+flavor-base-trashjuice = trashy
diff --git a/Resources/Locale/en-US/_NF/reagents/foods.ftl b/Resources/Locale/en-US/_NF/reagents/foods.ftl
index c4e85782ce8..bd9ed7bcfc9 100644
--- a/Resources/Locale/en-US/_NF/reagents/foods.ftl
+++ b/Resources/Locale/en-US/_NF/reagents/foods.ftl
@@ -1 +1,4 @@
-reagent-name-flaverol = Flaverol
\ No newline at end of file
+reagent-name-flaverol = Flaverol
+
+reagent-name-trashjuice = trash juices
+reagent-desc-trashjuice = Do you really want to know what it is?
diff --git a/Resources/Locale/en-US/_NF/reagents/physical-desc.ftl b/Resources/Locale/en-US/_NF/reagents/physical-desc.ftl
new file mode 100644
index 00000000000..24bb74a5ea4
--- /dev/null
+++ b/Resources/Locale/en-US/_NF/reagents/physical-desc.ftl
@@ -0,0 +1 @@
+reagent-physical-desc-trashjuice = ... Wait.. Did it just move?