Metamorphosis - FoodSequence 3 (#31012)
* setup some data * cheeseburger recipe * Update FoodSequenceSystem.cs * finalize cheseburger recipe * remove fun * return old taco sprites * full foodsequence data refactor * return tacos * well done * add cutlets to burger * chickenburger recipe * +2 burger recipes * more fun * Update brain.png * some slice produce added * documentation * watermelon * skewer work * flipping * tomato * skewer watermelon * Update skewer.yml * oopsie, ok, im go to sleep * fix checks * Update produce.yml * screwed * cheeeeeeeese * all cooked meat added * produce added * aaaaand suppermatter * key to Tag * More * proto string remove * raw snail * fix * Update FoodMetamorphableByAddingComponent.cs * fixes * fix3 * fififififx
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Nutrition.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to metamorphose a modular food when a new ingredient is added.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedFoodSequenceSystem))]
|
||||
public sealed partial class FoodMetamorphableByAddingComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// if true, the metamorphosis will only be attempted when the sequence ends, not when each element is added.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool OnlyFinal = true;
|
||||
}
|
||||
@@ -1,55 +1,25 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Nutrition.Prototypes;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Nutrition.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Tndicates that this entity can be inserted into FoodSequence, which will transfer all reagents to the target.
|
||||
/// Indicates that this entity can be inserted into FoodSequence, which will transfer all reagents to the target.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SharedFoodSequenceSystem))]
|
||||
public sealed partial class FoodSequenceElementComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// the same object can be used in different sequences, and it will have a different sprite in different sequences.
|
||||
/// The same object can be used in different sequences, and it will have a different data in then.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public Dictionary<string, FoodSequenceElementEntry> Entries = new();
|
||||
public Dictionary<ProtoId<TagPrototype>, ProtoId<FoodSequenceElementPrototype>> Entries = new();
|
||||
|
||||
/// <summary>
|
||||
/// which solution we will add to the main dish
|
||||
/// Which solution we will add to the main dish
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string Solution = "food";
|
||||
|
||||
/// <summary>
|
||||
/// state used to generate the appearance of the added layer
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SpriteSpecifier? Sprite;
|
||||
}
|
||||
|
||||
[DataRecord, Serializable, NetSerializable]
|
||||
public sealed class FoodSequenceElementEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// A localized name piece to build into the item name generator.
|
||||
/// </summary>
|
||||
public LocId? Name { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// overriding default sprite
|
||||
/// </summary>
|
||||
public SpriteSpecifier? Sprite { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// If the layer is the final one, it can be added over the limit, but no other layers can be added after it.
|
||||
/// </summary>
|
||||
public bool Final { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// the shear of a particular layer. Allows a little "randomization" of each layer.
|
||||
/// </summary>
|
||||
public Vector2 LocalOffset { get; set; } = Vector2.Zero;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Nutrition.Prototypes;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Nutrition.Components;
|
||||
|
||||
@@ -14,7 +19,7 @@ public sealed partial class FoodSequenceStartPointComponent : Component
|
||||
/// A key that determines which types of food elements can be attached to a food.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public string Key = string.Empty;
|
||||
public ProtoId<TagPrototype> Key = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of layers of food that can be placed on this item.
|
||||
@@ -22,62 +27,20 @@ public sealed partial class FoodSequenceStartPointComponent : Component
|
||||
[DataField]
|
||||
public int MaxLayers = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Start shift from the center of the sprite where the first layer of food will be placed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 StartPosition = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Shift from the start position applied to each subsequent layer.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 Offset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Can we put more layers?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Finished;
|
||||
|
||||
/// <summary>
|
||||
/// list of sprite states to be displayed on this object.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<FoodSequenceElementEntry> FoodLayers = new();
|
||||
|
||||
public HashSet<string> RevealedLayers = new();
|
||||
|
||||
/// <summary>
|
||||
/// target layer, where new layers will be added. This allows you to control the order of generative layers and static layers.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string TargetLayerMap = "foodSequenceLayers";
|
||||
|
||||
/// <summary>
|
||||
/// If true, the generative layers will be placed in reverse order.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool InverseLayers;
|
||||
|
||||
/// <summary>
|
||||
/// each layer will get a random offset in the specified range
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 MaxLayerOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// each layer will get a random offset in the specified range
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 MinLayerOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// solution where reagents will be added from newly added ingredients
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string Solution = "food";
|
||||
|
||||
#region name generation
|
||||
|
||||
/// <summary>
|
||||
/// LocId with a name generation pattern.
|
||||
/// </summary>
|
||||
@@ -101,4 +64,96 @@ public sealed partial class FoodSequenceStartPointComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? NameSuffix;
|
||||
|
||||
#endregion
|
||||
|
||||
#region visual
|
||||
|
||||
/// <summary>
|
||||
/// list of sprite states to be displayed on this object.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<FoodSequenceVisualLayer> FoodLayers = new();
|
||||
|
||||
/// <summary>
|
||||
/// If true, the generative layers will be placed in reverse order.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool InverseLayers;
|
||||
|
||||
/// <summary>
|
||||
/// target layer, where new layers will be added. This allows you to control the order of generative layers and static layers.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string TargetLayerMap = "foodSequenceLayers";
|
||||
|
||||
/// <summary>
|
||||
/// Start shift from the center of the sprite where the first layer of food will be placed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 StartPosition = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Shift from the start position applied to each subsequent layer.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 Offset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// each layer will get a random offset in the specified range
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 MaxLayerOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// each layer will get a random offset in the specified range
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2 MinLayerOffset = Vector2.Zero;
|
||||
|
||||
[DataField]
|
||||
public bool AllowHorizontalFlip = true;
|
||||
|
||||
public HashSet<string> RevealedLayers = new();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// class that synchronizes with the client
|
||||
/// Stores all the necessary information for rendering the FoodSequence element
|
||||
/// </summary>
|
||||
[DataRecord, Serializable, NetSerializable]
|
||||
public record struct FoodSequenceVisualLayer
|
||||
{
|
||||
/// <summary>
|
||||
/// reference to the original prototype of the layer. Used to edit visual layers.
|
||||
/// </summary>
|
||||
public ProtoId<FoodSequenceElementPrototype> Proto;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite rendered in sequence
|
||||
/// </summary>
|
||||
public SpriteSpecifier? Sprite { get; set; } = SpriteSpecifier.Invalid;
|
||||
|
||||
/// <summary>
|
||||
/// Relative size of the sprite displayed in FoodSequence
|
||||
/// </summary>
|
||||
public Vector2 Scale { get; set; } = Vector2.One;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of a particular layer. Allows a little position randomization of each layer.
|
||||
/// </summary>
|
||||
public Vector2 LocalOffset { get; set; } = Vector2.Zero;
|
||||
|
||||
public FoodSequenceVisualLayer(ProtoId<FoodSequenceElementPrototype> proto,
|
||||
SpriteSpecifier? sprite,
|
||||
Vector2 scale,
|
||||
Vector2 offset)
|
||||
{
|
||||
Proto = proto;
|
||||
Sprite = sprite;
|
||||
Scale = scale;
|
||||
LocalOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Nutrition;
|
||||
@@ -67,3 +70,8 @@ public record struct SliceFoodEvent();
|
||||
public sealed partial class SliceFoodDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on FoodSequence start element entity when new ingredient is added to FoodSequence
|
||||
/// </summary>
|
||||
public record struct FoodSequenceIngredientAddedEvent(EntityUid Start, EntityUid Element, ProtoId<FoodSequenceElementPrototype> Proto, EntityUid? User = null);
|
||||
|
||||
218
Content.Shared/Nutrition/FoodMetamorphRules/FoodMetamorphRule.cs
Normal file
218
Content.Shared/Nutrition/FoodMetamorphRules/FoodMetamorphRule.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Destructible.Thresholds;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Tag;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Nutrition.FoodMetamorphRules;
|
||||
|
||||
/// <summary>
|
||||
/// abstract rules that are used to verify the correct foodSequence for recipe
|
||||
/// </summary>
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
[Serializable, NetSerializable]
|
||||
public abstract partial class FoodMetamorphRule
|
||||
{
|
||||
public abstract bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The requirement that the sequence be within the specified size limit
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class SequenceLength : FoodMetamorphRule
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public MinMax Range;
|
||||
|
||||
public override bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients)
|
||||
{
|
||||
return ingredients.Count <= Range.Max && ingredients.Count >= Range.Min;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A requirement that the last element of the sequence have one or all of the required tags
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class LastElementHasTags : FoodMetamorphRule
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<TagPrototype>> Tags = new ();
|
||||
|
||||
[DataField]
|
||||
public bool NeedAll = true;
|
||||
|
||||
public override bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients)
|
||||
{
|
||||
var lastIngredient = ingredients[ingredients.Count - 1];
|
||||
|
||||
if (!protoMan.TryIndex(lastIngredient.Proto, out var protoIndexed))
|
||||
return false;
|
||||
|
||||
foreach (var tag in Tags)
|
||||
{
|
||||
var containsTag = protoIndexed.Tags.Contains(tag);
|
||||
|
||||
if (NeedAll && !containsTag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NeedAll && containsTag)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return NeedAll;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A requirement that the specified sequence element have one or all of the required tags
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ElementHasTags : FoodMetamorphRule
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public int ElementNumber = 0;
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<TagPrototype>> Tags = new ();
|
||||
|
||||
[DataField]
|
||||
public bool NeedAll = true;
|
||||
|
||||
public override bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients)
|
||||
{
|
||||
if (ingredients.Count < ElementNumber + 1)
|
||||
return false;
|
||||
|
||||
if (!protoMan.TryIndex(ingredients[ElementNumber].Proto, out var protoIndexed))
|
||||
return false;
|
||||
|
||||
foreach (var tag in Tags)
|
||||
{
|
||||
var containsTag = protoIndexed.Tags.Contains(tag);
|
||||
|
||||
if (NeedAll && !containsTag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NeedAll && containsTag)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return NeedAll;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// requirement that the food contains certain reagents (e.g. sauces)
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class FoodHasReagent : FoodMetamorphRule
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public ProtoId<ReagentPrototype> Reagent = new();
|
||||
|
||||
[DataField(required: true)]
|
||||
public MinMax Count;
|
||||
|
||||
[DataField]
|
||||
public string Solution = "food";
|
||||
|
||||
public override bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients)
|
||||
{
|
||||
if (!entMan.TryGetComponent<SolutionContainerManagerComponent>(food, out var solMan))
|
||||
return false;
|
||||
|
||||
var solutionMan = entMan.System<SharedSolutionContainerSystem>();
|
||||
|
||||
if (!solutionMan.TryGetSolution(food, Solution, out var foodSoln, out var foodSolution))
|
||||
return false;
|
||||
|
||||
foreach (var (id, quantity) in foodSoln.Value.Comp.Solution.Contents)
|
||||
{
|
||||
if (id.Prototype != Reagent.Id)
|
||||
continue;
|
||||
|
||||
if (quantity < Count.Min || quantity > Count.Max)
|
||||
break;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A requirement that there be X ingredients in the sequence that have one or all of the specified tags.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class IngredientsWithTags : FoodMetamorphRule
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<TagPrototype>> Tags = new ();
|
||||
|
||||
[DataField(required: true)]
|
||||
public MinMax Count = new();
|
||||
|
||||
[DataField]
|
||||
public bool NeedAll = true;
|
||||
|
||||
public override bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var ingredient in ingredients)
|
||||
{
|
||||
if (!protoMan.TryIndex(ingredient.Proto, out var protoIndexed))
|
||||
continue;
|
||||
|
||||
var allowed = false;
|
||||
if (NeedAll)
|
||||
{
|
||||
allowed = true;
|
||||
foreach (var tag in Tags)
|
||||
{
|
||||
if (!protoIndexed.Tags.Contains(tag))
|
||||
{
|
||||
allowed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
allowed = false;
|
||||
foreach (var tag in Tags)
|
||||
{
|
||||
if (protoIndexed.Tags.Contains(tag))
|
||||
{
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count >= Count.Min && count <= Count.Max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Nutrition.Prototypes;
|
||||
|
||||
/// <summary>
|
||||
/// Unique data storage block for different FoodSequence layers
|
||||
/// </summary>
|
||||
[Prototype("foodSequenceElement")]
|
||||
public sealed partial class FoodSequenceElementPrototype : IPrototype
|
||||
{
|
||||
[IdDataField] public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// sprite options. A random one will be selected and used to display the layer.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<SpriteSpecifier> Sprites { get; private set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A localized name piece to build into the item name generator.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the layer is the final one, it can be added over the limit, but no other layers can be added after it.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Final { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tag list of this layer. Used for recipes for food metamorphosis.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<TagPrototype>> Tags { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Content.Shared.Nutrition.FoodMetamorphRules;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Nutrition.Prototypes;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a recipe so that FoodSequence assembled in the right sequence can turn into a special meal.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class MetamorphRecipePrototype : IPrototype
|
||||
{
|
||||
[IdDataField] public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The key of the FoodSequence being collected. For example “burger” “taco” etc.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<TagPrototype> Key = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The entity that will be created as a result of this recipe, and into which all the reagents will be transferred.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Result = default!;
|
||||
|
||||
/// <summary>
|
||||
/// A sequence of rules that must be followed for FoodSequence to metamorphose into a special food.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<FoodMetamorphRule> Rules = new();
|
||||
}
|
||||
Reference in New Issue
Block a user