Skip to content

Commit

Permalink
Metamorphosis - FoodSequence 3 (space-wizards#31012)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
TheShuEd authored and sleepyyapril committed Dec 16, 2024
1 parent 2115843 commit 061fb3c
Show file tree
Hide file tree
Showing 40 changed files with 1,331 additions and 959 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ private void UpdateFoodVisuals(Entity<FoodSequenceStartPointComponent> start, Sp

//Set image
sprite.LayerSetSprite(index, state.Sprite);
sprite.LayerSetScale(index, state.Scale);

//Offset the layer
var layerPos = start.Comp.StartPosition;
Expand Down
176 changes: 150 additions & 26 deletions Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Nutrition.Prototypes;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;

namespace Content.Server.Nutrition.EntitySystems;

Expand All @@ -13,12 +20,19 @@ public sealed class FoodSequenceSystem : SharedFoodSequenceSystem
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly TransformSystem _transform = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<FoodSequenceStartPointComponent, InteractUsingEvent>(OnInteractUsing);

SubscribeLocalEvent<FoodMetamorphableByAddingComponent, FoodSequenceIngredientAddedEvent>(OnIngredientAdded);
}

private void OnInteractUsing(Entity<FoodSequenceStartPointComponent> ent, ref InteractUsingEvent args)
Expand All @@ -27,42 +41,124 @@ private void OnInteractUsing(Entity<FoodSequenceStartPointComponent> ent, ref In
TryAddFoodElement(ent, (args.Used, sequenceElement), args.User);
}

private bool TryAddFoodElement(Entity<FoodSequenceStartPointComponent> start, Entity<FoodSequenceElementComponent> element, EntityUid? user = null)
private void OnIngredientAdded(Entity<FoodMetamorphableByAddingComponent> ent, ref FoodSequenceIngredientAddedEvent args)
{
if (!TryComp<FoodSequenceStartPointComponent>(args.Start, out var start))
return;

if (!_proto.TryIndex(args.Proto, out var elementProto))
return;

if (!ent.Comp.OnlyFinal || elementProto.Final || start.FoodLayers.Count == start.MaxLayers)
{
TryMetamorph((ent, start));
}
}

private bool TryMetamorph(Entity<FoodSequenceStartPointComponent> start)
{
FoodSequenceElementEntry? elementData = null;
foreach (var entry in element.Comp.Entries)
List<MetamorphRecipePrototype> availableRecipes = new();
foreach (var recipe in _proto.EnumeratePrototypes<MetamorphRecipePrototype>())
{
if (entry.Key == start.Comp.Key)
if (recipe.Key != start.Comp.Key)
continue;

bool allowed = true;
foreach (var rule in recipe.Rules)
{
elementData = entry.Value;
break;
if (!rule.Check(_proto, EntityManager, start, start.Comp.FoodLayers))
{
allowed = false;
break;
}
}
if (allowed)
availableRecipes.Add(recipe);
}

if (elementData is null)
if (availableRecipes.Count <= 0)
return true;

Metamorf(start, _random.Pick(availableRecipes)); //In general, if there's more than one recipe, the yml-guys screwed up. Maybe some kind of unit test is needed.
QueueDel(start);
return true;
}

private void Metamorf(Entity<FoodSequenceStartPointComponent> start, MetamorphRecipePrototype recipe)
{
var result = SpawnAtPosition(recipe.Result, Transform(start).Coordinates);

//Try putting in container
_transform.DropNextTo(result, (start, Transform(start)));

if (!_solutionContainer.TryGetSolution(result, start.Comp.Solution, out var resultSoln, out var resultSolution))
return;

if (!_solutionContainer.TryGetSolution(start.Owner, start.Comp.Solution, out var startSoln, out var startSolution))
return;

_solutionContainer.RemoveAllSolution(resultSoln.Value); //Remove all YML reagents
resultSoln.Value.Comp.Solution.MaxVolume = startSoln.Value.Comp.Solution.MaxVolume;
_solutionContainer.TryAddSolution(resultSoln.Value, startSolution);

MergeFlavorProfiles(start, result);
MergeTrash(start, result);
MergeTags(start, result);
}

private bool TryAddFoodElement(Entity<FoodSequenceStartPointComponent> start, Entity<FoodSequenceElementComponent> element, EntityUid? user = null)
{
// we can't add a live mouse to a burger.
if (!TryComp<FoodComponent>(element, out var elementFood))
return false;
if (elementFood.RequireDead && _mobState.IsAlive(element))
return false;

//looking for a suitable FoodSequence prototype
ProtoId<FoodSequenceElementPrototype> elementProto = string.Empty;
foreach (var pair in element.Comp.Entries)
{
if (pair.Key == start.Comp.Key)
{
elementProto = pair.Value;
}
}
if (!_proto.TryIndex(elementProto, out var elementIndexed))
return false;

//if we run out of space, we can still put in one last, final finishing element.
if (start.Comp.FoodLayers.Count >= start.Comp.MaxLayers && !elementData.Value.Final || start.Comp.Finished)
if (start.Comp.FoodLayers.Count >= start.Comp.MaxLayers && !elementIndexed.Final || start.Comp.Finished)
{
if (user is not null)
_popup.PopupEntity(Loc.GetString("food-sequence-no-space"), start, user.Value);
return false;
}

if (elementData.Value.Sprite is not null)
{
start.Comp.FoodLayers.Add(elementData.Value);
Dirty(start);
}
//Generate new visual layer
var flip = start.Comp.AllowHorizontalFlip && _random.Prob(0.5f);
var layer = new FoodSequenceVisualLayer(elementIndexed,
_random.Pick(elementIndexed.Sprites),
new Vector2(flip ? -1 : 1, 1),
new Vector2(
_random.NextFloat(start.Comp.MinLayerOffset.X, start.Comp.MaxLayerOffset.X),
_random.NextFloat(start.Comp.MinLayerOffset.Y, start.Comp.MaxLayerOffset.Y))
);

start.Comp.FoodLayers.Add(layer);
Dirty(start);

if (elementData.Value.Final)
if (elementIndexed.Final)
start.Comp.Finished = true;

UpdateFoodName(start);
MergeFoodSolutions(start, element);
MergeFlavorProfiles(start, element);
MergeTrash(start, element);
MergeTags(start, element);

var ev = new FoodSequenceIngredientAddedEvent(start, element, elementProto, user);
RaiseLocalEvent(start, ev);

QueueDel(element);
return true;
}
Expand All @@ -77,16 +173,27 @@ private void UpdateFoodName(Entity<FoodSequenceStartPointComponent> start)
if (start.Comp.ContentSeparator is not null)
separator = Loc.GetString(start.Comp.ContentSeparator);

HashSet<LocId> existedContentNames = new();
HashSet<ProtoId<FoodSequenceElementPrototype>> existedContentNames = new();
foreach (var layer in start.Comp.FoodLayers)
{
if (layer.Name is not null && !existedContentNames.Contains(layer.Name.Value))
{
content.Append(Loc.GetString(layer.Name.Value));
existedContentNames.Add(layer.Name.Value);
}
if (!existedContentNames.Contains(layer.Proto))
existedContentNames.Add(layer.Proto);
}

var nameCounter = 1;
foreach (var proto in existedContentNames)
{
if (!_proto.TryIndex(proto, out var protoIndexed))
continue;

content.Append(separator);
if (protoIndexed.Name is null)
continue;

content.Append(Loc.GetString(protoIndexed.Name.Value));

if (nameCounter < existedContentNames.Count)
content.Append(separator);
nameCounter++;
}

var newName = Loc.GetString(start.Comp.NameGeneration.Value,
Expand All @@ -97,19 +204,25 @@ private void UpdateFoodName(Entity<FoodSequenceStartPointComponent> start)
_metaData.SetEntityName(start, newName);
}

private void MergeFoodSolutions(Entity<FoodSequenceStartPointComponent> start, Entity<FoodSequenceElementComponent> element)
private void MergeFoodSolutions(EntityUid start, EntityUid element)
{
if (!_solutionContainer.TryGetSolution(start.Owner, start.Comp.Solution, out var startSolutionEntity, out var startSolution))
if (!TryComp<FoodComponent>(start, out var startFood))
return;

if (!TryComp<FoodComponent>(element, out var elementFood))
return;

if (!_solutionContainer.TryGetSolution(start, startFood.Solution, out var startSolutionEntity, out var startSolution))
return;

if (!_solutionContainer.TryGetSolution(element.Owner, element.Comp.Solution, out _, out var elementSolution))
if (!_solutionContainer.TryGetSolution(element, elementFood.Solution, out _, out var elementSolution))
return;

startSolution.MaxVolume += elementSolution.MaxVolume;
_solutionContainer.TryAddSolution(startSolutionEntity.Value, elementSolution);
}

private void MergeFlavorProfiles(Entity<FoodSequenceStartPointComponent> start, Entity<FoodSequenceElementComponent> element)
private void MergeFlavorProfiles(EntityUid start, EntityUid element)
{
if (!TryComp<FlavorProfileComponent>(start, out var startProfile))
return;
Expand All @@ -124,7 +237,7 @@ private void MergeFlavorProfiles(Entity<FoodSequenceStartPointComponent> start,
}
}

private void MergeTrash(Entity<FoodSequenceStartPointComponent> start, Entity<FoodSequenceElementComponent> element)
private void MergeTrash(EntityUid start, EntityUid element)
{
if (!TryComp<FoodComponent>(start, out var startFood))
return;
Expand All @@ -134,5 +247,16 @@ private void MergeTrash(Entity<FoodSequenceStartPointComponent> start, Entity<Fo

foreach (var trash in elementFood.Trash)
startFood.Trash.Add(trash);
}
}

private void MergeTags(EntityUid start, EntityUid element)
{
if (!TryComp<TagComponent>(element, out var elementTags))
return;

EnsureComp<TagComponent>(start);

_tag.TryAddTags(start, elementTags.Tags);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
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";
Expand Down
Loading

0 comments on commit 061fb3c

Please sign in to comment.