Skip to content

Commit

Permalink
#TajaranLivesMatter, часть первая: Диета (#267)
Browse files Browse the repository at this point in the history
* Added accumulation ReagentEffect for theobromine

* Removed starvation damage from the tajaran race

* Added diet-specific metabolizer types for the tajaran and vulpkanin races

* Added theobromine into some beverages containing coffee

* Added translation to the guidebook

* Added PoorlyDigestibleFood system

* Removed metabolizer types previously added

* Added IndigestibleFiber reagent

* Retagged pizza to conform to the new system

* Added Meat tag for the dumplings

* Fixed formatting to match LPP

* Fixed minor typos

* Fixed minor typos

* Fixed extra import packaging error

---------

Co-authored-by: Evgencheg <[email protected]>
  • Loading branch information
NeLepus and Evgencheg authored Oct 21, 2024
1 parent bb4f79d commit 221a2ae
Show file tree
Hide file tree
Showing 20 changed files with 337 additions and 11 deletions.
7 changes: 7 additions & 0 deletions Content.Server/Body/Components/StomachComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ public sealed partial class StomachComponent : Component
[DataField]
public EntityWhitelist? SpecialDigestible = null;

/// <summary>
/// Добавлено LostParadise.
/// Data responsible to describe what foods this stomach is incapable of processing efficiently.
/// </summary>
[DataField("poorlyDigestibleFood")]
public PoorlyDigestibleFood? LPP_PoorlyDigestibleFood = null;

/// <summary>
/// Used to track how long each reagent has been in the stomach
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions Content.Server/Nutrition/EntitySystems/FoodSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ private void OnDoAfter(Entity<FoodComponent> entity, ref ConsumeDoAfterEvent arg
}

_reaction.DoEntityReaction(args.Target.Value, solution, ReactionMethod.Ingestion);

// Добавлено LostParadise.
if (stomachToUse.LPP_PoorlyDigestibleFood?.AffectsFood(entity, EntityManager) == true)
split = stomachToUse.LPP_PoorlyDigestibleFood.ModifySolution(split);

_stomach.TryTransferSolution(stomachToUse.Owner, split, stomachToUse);

var flavors = args.FlavorMessage;
Expand Down
112 changes: 112 additions & 0 deletions Content.Server/_LostParadise/CarnivoresDiet/AccumulateReagent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using Content.Server.Body.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server.Chemistry.ReagentEffects
{
// This class is basically copied from the sealed class AdjustReagent, but compensates a reagent's metabolism rates when adjusting its levels.
// Was introduced in order to make theobromine accumulate faster than it metabolises.
// While quite kludgy, it is still much safer and cleaner alternative to actively preventing the metabolism system from processing theobromine while it's still in the system.

// TL;DR Это костыль, который тупо добавляет столько же реагента, сколько должно впитаться за данный тик.
// Решение сомнительное, но оно гораздо чище, чем лезть в код метаболирования и добавлять туда группу-исключение с проверкой на наличие реагента в кровеносной системе.

[UsedImplicitly]
public sealed partial class AccumulateReagent : ReagentEffect
{
/// <summary>
/// The reagent ID to accumulate. Only one of this and <see cref="Group"/> should be active.
/// </summary>
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>), required: true)]
public string? Reagent;

/// <summary>
/// Checks if the target has something that already adds the target reagent
/// </summary>
public bool ContainsPositiveAdjustEffect(IPrototypeManager prototypeMan, Solution solution, List<MetabolismGroupEntry> groups)
{
foreach (var quantity in solution.Contents)
{
var reagent = quantity.Reagent.Prototype;
if (reagent == Reagent)
continue;

if (!prototypeMan.TryIndex(reagent, out ReagentPrototype? reagentProto))
continue;

if (reagentProto?.Metabolisms == null)
continue;

// Ideally we should iterate over the body's MetabolismGroupEntry list.
// But I have no idea why there's no Drink in its .MetabolismGroups property and how to fetch that.
// So it will stay like this for now, but might cause unintended theobromine accumulation in some *very* unlikely and specific scenarios.
foreach (var reagentEffectsEntry in reagentProto.Metabolisms.Values)
{
foreach (var effect in reagentEffectsEntry.Effects)
{
if (effect is not AdjustReagent adjustReagent)
continue;

if (adjustReagent.Reagent == Reagent)
return true;
}
}
}

return false;
}

public override void Effect(ReagentEffectArgs args)
{
// Source is where Theobromine is currently coming from
if (args.Source == null)
return;

if (Reagent == null)
return;

var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
prototypeMan.TryIndex(Reagent, out ReagentPrototype? reagentProto);

args.EntityManager.TryGetComponent(args.OrganEntity, out MetabolizerComponent? metabolizer);

if (metabolizer?.MetabolismGroups is not List<MetabolismGroupEntry> groups)
return;

if (!ContainsPositiveAdjustEffect(prototypeMan, args.Source, groups))
return;

if (reagentProto?.Metabolisms == null)
return;

FixedPoint2 totalCompensationRate = 0;
foreach (var group in groups)
{
// Normally, the rate should only be processed once since a reagent usually only has one group.
if (!reagentProto.Metabolisms.TryGetValue(group.Id, out var reagentEffectsEntry))
continue;

var groupRate = reagentEffectsEntry.MetabolismRate * group.MetabolismRateModifier;
totalCompensationRate += groupRate;
}

// amount *= args.Scale;
args.Source.AddReagent(Reagent, totalCompensationRate);
}

protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
if (Reagent != null && prototype.TryIndex(Reagent, out ReagentPrototype? reagentProto))
{
return Loc.GetString("reagent-effect-guidebook-accumulate-reagent-reagent",
("reagent", reagentProto.LocalizedName));
}

throw new NotImplementedException();
}
}
}
92 changes: 92 additions & 0 deletions Content.Server/_LostParadise/CarnivoresDiet/PoorlyDigestible.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.Tag;
using Content.Shared.Whitelist;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Content.Server.Body.Components
{
[DataDefinition]
public sealed partial class PoorlyDigestibleFood
{
/// <summary>
/// Reagents that get multiplied by the Factor field.
/// </summary>
[DataField("reducedReagents")]
public List<string>? ReducedReagents = null;

/// <summary>
/// Food containing at least one of these tags is affected as long as it doesn't hit any BlacklistTags.
/// </summary>
[DataField("whitelistTags", customTypeSerializer: typeof(PrototypeIdListSerializer<TagPrototype>))]
public List<string>? WhitelistTags = null;

/// <summary>
/// Food containing any of these tags is not affected.
/// </summary>
[DataField("blacklistTags", customTypeSerializer: typeof(PrototypeIdListSerializer<TagPrototype>))]
public List<string>? BlacklistTags = null;

/// <summary>
/// Factor that the reagent quantities are multiplied by.
/// </summary>
[DataField("factor")]
public FixedPoint2 Factor = 1f;

/// <summary>
/// Reagent that is used to replace the volume taken away by the Factor multiplication.
/// </summary>
[DataField("replacementReagent")]
public string? ReplacementReagentID = null;

public bool AffectsFood(EntityUid foodEnt, IEntityManager entityManager)
{
if (WhitelistTags is null)
return false;

var tagSys = entityManager.System<TagSystem>();

if (BlacklistTags is not null && tagSys.HasAnyTag(foodEnt, BlacklistTags))
return false;

return tagSys.HasAnyTag(foodEnt, WhitelistTags);
}

public Solution ModifySolution(Solution solution)
{
// In case someone forgot to set the reagents being replaced, we do nothing.
if (ReducedReagents is null || Factor <= 0f)
return solution;

// Recreating the list since we can't set quentities in-place.
List<ReagentQuantity> newReagents = new();
FixedPoint2 removedQuantity = 0f;
foreach (var quantity in solution.Contents)
{
if (!ReducedReagents.Contains(quantity.Reagent.Prototype))
{
newReagents.Add(quantity);
continue;
}

removedQuantity += quantity.Quantity;
newReagents.Add(new ReagentQuantity(quantity.Reagent, quantity.Quantity * Factor));
}
removedQuantity *= 1f - Factor;

if (ReplacementReagentID is string reagentId)
newReagents.Add(new ReagentQuantity(new ReagentId(reagentId, null), removedQuantity));

solution.SetContents(newReagents);
return solution;
}
}
}
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/_LostParadise/Guidebook/effects.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reagent-effect-guidebook-accumulate-reagent-reagent =
Accumulates as long as there's a {$reagent} source present in the bloodstream
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reagent-name-indigestible-fibre = indigestible dietary fibre
reagent-desc-indigestible-fibre = Residues of a digestive system poorly adapted to plant-derived food.
2 changes: 2 additions & 0 deletions Resources/Locale/ru-RU/_LostParadise/Guidebook/effects.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reagent-effect-guidebook-accumulate-reagent-reagent =
Накапливается в кровеносной системе, пока в ней имеется вещество, добавляющее {$reagent}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reagent-name-indigestible-fibre = неперевариваемые растительные волокна
reagent-desc-indigestible-fibre = Остаточные продукты пищеварительной системы, плохо приспособленной к растительной пище.
20 changes: 20 additions & 0 deletions Resources/Prototypes/DeltaV/Body/Organs/vulpkanin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@
noSpawn: true
components:
- type: Stomach
poorlyDigestibleFood:
reducedReagents:
- Nutriment
- Vitamin
whitelistTags:
- Fruit # LostParadise's lore states the vulpkanin race isn't really adapted to plant food
- Vegetable
- Bread
- Pizza # Basic pizza is also bakery
- Wheat
blacklistTags:
- Meat # Meat-based food is excluded in order not to force the carnivores to be meat-purists
factor: 0.25
replacementReagent: LPPIndigestibleFiber
- type: SolutionContainerManager
solutions:
stomach:
Expand All @@ -13,3 +27,9 @@
reagents:
- ReagentId: UncookedAnimalProteins
Quantity: 5
- type: Metabolizer
maxReagents: 3
metabolizerTypes: [ Animal ]
groups:
- id: Food
- id: Drink
Original file line number Diff line number Diff line change
Expand Up @@ -2383,3 +2383,6 @@
effects:
- !type:SatiateThirst
factor: 4
- !type:AdjustReagent
reagent: Theobromine
amount: 0.04
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@
Quantity: 8
- ReagentId: Protein
Quantity: 2
- type: Tag
tags:
- Meat

- type: entity
name: chèvre chaud
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
- state: margherita
- type: SliceableFood
slice: FoodPizzaMargheritaSlice
- type: Tag
tags:
- Pizza
- Vegetable

- type: entity
name: slice of margherita pizza
Expand All @@ -93,6 +97,10 @@
- type: Sprite
layers:
- state: margherita-slice
- type: Tag
tags:
- Pizza
- Vegetable

- type: entity
name: meat pizza
Expand Down Expand Up @@ -200,7 +208,11 @@
Quantity: 5
- ReagentId: Vitamin
Quantity: 5

- type: Tag
tags:
- Pizza
- Vegetable

- type: entity
name: slice of vegetable pizza
parent: FoodPizzaSliceBase
Expand Down Expand Up @@ -230,6 +242,10 @@
Quantity: 1
- ReagentId: Vitamin
Quantity: 1
- type: Tag
tags:
- Pizza
- Vegetable

# Tastes like crust, tomato, cheese, carrot.

Expand Down Expand Up @@ -321,6 +337,10 @@
Quantity: 5
- ReagentId: DoctorsDelight
Quantity: 6
- type: Tag
tags:
- Pizza
- Vegetable

- type: entity
name: slice of dank pizza
Expand Down Expand Up @@ -348,6 +368,10 @@
Quantity: 0.8
- ReagentId: DoctorsDelight
Quantity: 1
- type: Tag
tags:
- Pizza
- Vegetable
# Tastes like crust, tomato, cheese, meat, satisfaction.

- type: entity
Expand Down Expand Up @@ -411,6 +435,10 @@
- state: pineapple
- type: SliceableFood
slice: FoodPizzaPineappleSlice
- type: Tag
tags:
- Meat
- Pizza

- type: entity
name: slice of pineapple pizza
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- type: Tag
tags:
- Pizza
- Vegetable # Yes, I am aware that corn is technically a fruit, but the tagging system is enough of a mess as it is

- type: entity
name: slice of corncob crust pizza
Expand All @@ -49,6 +50,7 @@
- type: Tag
tags:
- Pizza
- Vegetable

- type: entity
name: firecracker pizza
Expand Down
Loading

0 comments on commit 221a2ae

Please sign in to comment.