Add cheese & dough reactions (#5547)

* git mv

* rename CreateEntityReaction -> CreateEntityTileReaction

* cheese

* change microwave recipes

* remove unnecessary test

* TryGet->Get
This commit is contained in:
Leon Friedrich
2021-11-27 11:50:14 +13:00
committed by GitHub
parent 7ecd08b8ca
commit 61423be24e
14 changed files with 169 additions and 91 deletions

View File

@@ -1,43 +0,0 @@
using System.Threading.Tasks;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Kitchen;
using NUnit.Framework;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Kitchen
{
[TestFixture]
public class KitchenTest : ContentIntegrationTest
{
[Test]
public async Task TestRecipesValid()
{
var server = StartServer();
await server.WaitIdleAsync();
var protoManager = server.ResolveDependency<IPrototypeManager>();
await server.WaitAssertion(() =>
{
foreach (var recipe in protoManager.EnumeratePrototypes<FoodRecipePrototype>())
{
Assert.That(protoManager.HasIndex<EntityPrototype>(recipe.Result), $"Cannot find FoodRecipe result {recipe.Result} in {recipe.ID}");
foreach (var (solid, amount) in recipe.IngredientsSolids)
{
Assert.That(protoManager.HasIndex<EntityPrototype>(solid), $"Cannot find FoodRecipe solid {solid} in {recipe.ID}");
Assert.That(amount > 0, $" FoodRecipe {recipe.ID} has invalid solid amount of {amount}");
}
foreach (var (reagent, amount) in recipe.IngredientsReagents)
{
Assert.That(protoManager.HasIndex<ReagentPrototype>(reagent), $"Cannot find FoodRecipe reagent {reagent} in {recipe.ID}");
Assert.That(amount > 0, $" FoodRecipe {recipe.ID} has invalid reagent amount of {amount}");
}
Assert.That(recipe.CookTime > 0, $"Cook time of {recipe.CookTime} for FoodRecipe {recipe.ID} is invalid!");
}
});
}
}
}

View File

@@ -0,0 +1,42 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Chemistry.ReactionEffects;
[DataDefinition]
public class CreateEntityReactionEffect : ReagentEffect
{
/// <summary>
/// What entity to create.
/// </summary>
[DataField("entity", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Entity = default!;
/// <summary>
/// How many entities to create per unit reaction.
/// </summary>
[DataField("number")]
public uint Number = 1;
public override void Effect(ReagentEffectArgs args)
{
var transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);
var quantity = Number * args.Quantity.Int();
for (var i = 0; i < quantity; i++)
{
args.EntityManager.SpawnEntity(Entity, transform.MapPosition);
// TODO figure out how to spawn inside of containers
// e.g. cheese:
// if the user is holding a bowl milk & enzyme, should drop to floor, not attached to the user.
// if reaction happens in a backpack, should insert cheese into backpack.
// --> if it doesn't fit, iterate through parent storage until it attaches to the grid (again, DON'T attach to players).
// if the reaction happens INSIDE a stomach? the bloodstream? I have no idea how to handle that.
// presumably having cheese materialize inside of your blood would have "disadvantages".
}
}
}

View File

@@ -16,7 +16,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
namespace Content.Server.Chemistry.TileReactions;
[DataDefinition]
public class CreateEntityReaction : ITileReaction
public class CreateEntityTileReaction : ITileReaction
{
[DataField("entity", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Entity = default!;

View File

@@ -45,6 +45,12 @@ namespace Content.Shared.Chemistry.Reaction
// TODO SERV3: Empty on the client, (de)serialize on the server with module manager is server module
[DataField("sound", serverOnly: true)] public SoundSpecifier Sound { get; private set; } = new SoundPathSpecifier("/Audio/Effects/Chemistry/bubbles.ogg");
/// <summary>
/// If true, this reaction will only consume only integer multiples of the reactant amounts. If there are not
/// enough reactants, the reaction does not occur. Useful for spawn-entity reactions (e.g. creating cheese).
/// </summary>
[DataField("quantized")] public bool Quantized = false;
}
/// <summary>

View File

@@ -46,6 +46,17 @@ namespace Content.Shared.Chemistry.Reaction
if (!solution.ContainsReagent(reactantName, out var reactantQuantity))
return false;
if (reactantData.Value.Catalyst)
{
// catalyst is not consumed, so will not limit the reaction. But it still needs to be present, and
// for quantized reactions we need to have a minimum amount
if (reactantQuantity == FixedPoint2.Zero || reaction.Quantized && reactantQuantity < reactantCoefficient)
return false;
continue;
}
var unitReactions = reactantQuantity / reactantCoefficient;
if (unitReactions < lowestUnitReactions)
@@ -53,7 +64,11 @@ namespace Content.Shared.Chemistry.Reaction
lowestUnitReactions = unitReactions;
}
}
return true;
if (reaction.Quantized)
lowestUnitReactions = (int) lowestUnitReactions;
return lowestUnitReactions > 0;
}
/// <summary>

View File

@@ -1,7 +1,10 @@
using System.Collections.Generic;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
using Robust.Shared.ViewVariables;
namespace Content.Shared.Kitchen
@@ -19,21 +22,21 @@ namespace Content.Shared.Kitchen
[DataField("name")]
private string _name = string.Empty;
[DataField("reagents")]
private readonly Dictionary<string, int> _ingsReagents = new();
[DataField("reagents", customTypeSerializer:typeof(PrototypeIdDictionarySerializer<uint, ReagentPrototype>))]
private readonly Dictionary<string, uint> _ingsReagents = new();
[DataField("solids")]
private readonly Dictionary<string, int> _ingsSolids = new ();
[DataField("solids", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<uint, EntityPrototype>))]
private readonly Dictionary<string, uint> _ingsSolids = new ();
[DataField("result")]
[DataField("result", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Result { get; } = string.Empty;
[DataField("time")]
public int CookTime { get; } = 5;
public uint CookTime { get; } = 5;
public string Name => Loc.GetString(_name);
public IReadOnlyDictionary<string, int> IngredientsReagents => _ingsReagents;
public IReadOnlyDictionary<string, int> IngredientsSolids => _ingsSolids;
public IReadOnlyDictionary<string, uint> IngredientsReagents => _ingsReagents;
public IReadOnlyDictionary<string, uint> IngredientsSolids => _ingsSolids;
}
}

View File

@@ -34,6 +34,14 @@
- id: MonkeyCubeBox
- id: SprayBottleWater
- id: ReagentContainerFlour
amount: 2
- id: ReagentContainerSugar
- id: FoodCondimentBottleEnzyme
# really, milk should go in the fridge. Unfortunately saltern only has freezers.
# yes, I'm using this as an excuse to not have to do extra work.
- id: ReagentContainerMilk
amount: 2
- id: ReagentContainerMilkSoy
- type: entity
id: ClosetJanitorFilled

View File

@@ -5,6 +5,7 @@
startingInventory:
ButchCleaver: 1
KitchenKnife: 5
FoodBowlBig: 5
DrinkGlass: 10
DrinkMug: 5
DrinkMugBlack: 2

View File

@@ -2,14 +2,14 @@
- type: entity
name: bowl
parent: BaseItem
parent: ReagentContainerBase
id: FoodBowlBig
description: A simple bowl, used for soups and salads.
components:
- type: SolutionContainerManager
solutions:
food:
maxVol: 20
maxVol: 50 # enough to make cheese in.
- type: Sprite
sprite: Objects/Consumable/Food/bowl.rsi
state: bowl
@@ -22,17 +22,11 @@
damage:
types:
Blunt: 5
- type: Spillable
solution: food
# I suspect this ought to be more of a transformable container kinda thing.
# For now, these components make it usable.
- type: Drink
solution: food
isOpen: true
- type: DrawableSolution
solution: food
- type: RefillableSolution
solution: food
- type: Damageable
damageContainer: Inorganic
- type: Destructible

View File

@@ -337,6 +337,7 @@
description: A thin glass bottle used to store condiments.
components:
- type: Drink
solution: food
openSounds:
collection: pop
- type: SolutionContainerManager
@@ -344,6 +345,8 @@
food:
maxVol: 30
- type: SolutionTransfer
canChangeTransferAmount: true
minTransferAmount: 5
maxTransferAmount: 30
- type: Sprite
netsync: false
@@ -480,6 +483,7 @@
description: A smaller glass bottle used to store condiments.
components:
- type: Drink
solution: food
openSounds:
collection: pop
- type: SolutionContainerManager

View File

@@ -214,10 +214,10 @@
- type: SolutionContainerManager
solutions:
food:
maxVol: 20
maxVol: 50 # at least enough for one cheese
reagents:
- ReagentId: Milk
Quantity: 20
Quantity: 50
- type: entity
parent: ReagentContainerBase
@@ -230,10 +230,10 @@
- type: SolutionContainerManager
solutions:
food:
maxVol: 20
maxVol: 50
reagents:
- ReagentId: MilkSoy
Quantity: 20
Quantity: 50
- type: entity
parent: ReagentContainerBase

View File

@@ -5,7 +5,7 @@
physicalDesc: fibrous
color: "#800000"
tileReactions:
- !type:CreateEntityReaction
- !type:CreateEntityTileReaction
entity: Carpet
maxOnTileWhitelist:
tags: [ Carpet ]
@@ -31,7 +31,7 @@
physicalDesc: buzzy
color: "#FFD35D"
tileReactions:
- !type:CreateEntityReaction
- !type:CreateEntityTileReaction
entity: MobBee
usage: 2
maxOnTile: 2

View File

@@ -4,19 +4,18 @@
name: cheeseburger recipe
result: FoodBurgerCheese
time: 5
reagents:
Flour: 5
solids:
FoodBreadPlainSlice: 2 # its not a burger bun, but its good enough
FoodMeat: 1
FoodCheeseSlice: 1
- type: microwaveMealRecipe
id: RecipeClownBurger
name: clownburger recipe
result: FoodBurgerClown
time: 5
reagents:
Flour: 5
solids:
FoodBreadPlainSlice: 2
ClothingMaskClown: 1
# - type: microwaveMealRecipe
@@ -34,9 +33,8 @@
name: xenoburger recipe
result: FoodBurgerXeno
time: 5
reagents:
Flour: 5
solids:
FoodBreadPlainSlice: 2
FoodMeatXeno: 1
#- type: microwaveMealRecipe
@@ -60,8 +58,8 @@
name: bread recipe
result: FoodBreadPlain
time: 15
reagents:
Flour: 15
solids:
FoodDough: 1
# - type: microwaveMealRecipe
# id: RecipeSandwich
@@ -105,9 +103,8 @@
name: cream cheese bread recipe
result: FoodBreadCreamcheese
time: 20
reagents:
Flour: 15
solids:
FoodDough: 1
FoodCheeseSlice: 2
- type: microwaveMealRecipe
@@ -115,11 +112,8 @@
name: banana bread recipe
result: FoodBreadBanana
time: 25
reagents:
Flour: 15
Milk: 5
solids:
FoodEgg: 3
FoodDough: 1
FoodBanana: 1
#Pizzas
@@ -128,9 +122,8 @@
name: margherita pizza recipe
result: FoodPizzaMargherita
time: 30
reagents:
Flour: 10
solids:
FoodDough: 1
FoodCheeseSlice: 4
FoodTomato: 1
@@ -139,9 +132,8 @@
name: mushroom pizza recipe
result: FoodPizzaMushroom
time: 25
reagents:
Flour: 10
solids:
FoodDough: 1
FoodMushroom: 5
- type: microwaveMealRecipe
@@ -149,9 +141,8 @@
name: meat pizza recipe
result: FoodPizzaMeat
time: 30
reagents:
Flour: 10
solids:
FoodDough: 1
FoodMeat: 3
FoodCheeseSlice: 1
FoodTomato: 1
@@ -161,9 +152,8 @@
name: vegetable pizza recipe
result: FoodPizzaVegetable
time: 30
reagents:
Flour: 10
solids:
FoodDough: 1
FoodEggplant: 1
FoodCarrot: 1
FoodCorn: 1
@@ -365,9 +355,8 @@
name: banana cream pie
result: FoodPieBananaCream
time: 15
reagents:
Flour: 10
solids:
FoodCakeBatter: 1 # should really be pie pastry or whatever. Good enough.
FoodBanana: 3
#Donks i guess

View File

@@ -0,0 +1,59 @@
- type: reaction
id: Curdling
impact: Low
quantized: true
reactants:
Milk:
amount: 40
Enzyme:
amount: 5
catalyst: true
effects:
- !type:CreateEntityReactionEffect
entity: FoodCheese
- type: reaction
id: CreateDough
impact: Low
quantized: true
reactants:
Flour:
amount: 15
Water:
amount: 10
effects:
- !type:CreateEntityReactionEffect
entity: FoodDough
- type: reaction
id: CreateCakeBatter
impact: Low
quantized: true
reactants:
Flour:
amount: 15
Egg:
amount: 12
Sugar:
amount: 5
effects:
- !type:CreateEntityReactionEffect
entity: FoodCakeBatter
# TG has a cake recipe that uses soy milk instead of eggs.
# but afaik it spawns the exact same cake batter entity.
# Maybe change this if you want to do allergies or something
- type: reaction
id: CreateVeganCakeBatter
impact: Low
quantized: true
reactants:
Flour:
amount: 15
MilkSoy:
amount: 12
Sugar:
amount: 5
effects:
- !type:CreateEntityReactionEffect
entity: FoodCakeBatter