Skip to content

Commit

Permalink
Port Food Recipe Guidebook (EE#783) (#2138)
Browse files Browse the repository at this point in the history
* Port EE#783 (Thank you, Mnemotechnician)

* Revert machine type changes to meal recipes

* Unneeded dependencies, show search bar

* Remove yet more microwave recipe machine types

* Food recipe guidebook: cleanup, style consistency

* Frontier comment cleanup

---------

Co-authored-by: Dvir <39403717+dvir001@users.noreply.github.com>
  • Loading branch information
whatston3 and dvir001 authored Oct 4, 2024
1 parent e431214 commit af2bf00
Show file tree
Hide file tree
Showing 15 changed files with 916 additions and 1 deletion.
21 changes: 21 additions & 0 deletions Content.Client/_EE/Guidebook/Controls/GuideFoodComposition.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal"
HorizontalAlignment="Stretch"
HorizontalExpand="True"
Margin="0 0 0 5">
<BoxContainer Name="ReactantsContainer" Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center">
<Label Name="ReagentLabel"
HorizontalExpand="True"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Access="Public"
Margin="25 0 0 0"/> <!-- Frontier: left margin 2<25 - matches reagent plant metabolism's stacked margins with GuideFoodEmbed -->
</BoxContainer>
<BoxContainer Orientation="Vertical" VerticalAlignment="Center">
<Label Name="AmountLabel"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Access="Public"
Margin="10 0 0 0"/> <!-- Frontier: left margin 2<10 -->
</BoxContainer>
</BoxContainer>
32 changes: 32 additions & 0 deletions Content.Client/_EE/Guidebook/Controls/GuideFoodComposition.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Content.Client.Guidebook.Controls;
using Content.Client.UserInterface.ControlExtensions;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client._EE.Guidebook.Controls; // Frontier: add _EE

[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideFoodComposition : BoxContainer, ISearchableControl
{
public GuideFoodComposition(ReagentPrototype proto, FixedPoint2 quantity)
{
RobustXamlLoader.Load(this);

ReagentLabel.Text = proto.LocalizedName;
AmountLabel.Text = quantity.ToString();
}

public bool CheckMatchesSearch(string query)
{
return this.ChildrenContainText(query);
}

public void SetHiddenState(bool state, string query)
{
Visible = CheckMatchesSearch(query) ? state : !state;
}
}
58 changes: 58 additions & 0 deletions Content.Client/_EE/Guidebook/Controls/GuideFoodEmbed.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Orientation="Vertical"
Margin="5 5 5 5">
<PanelContainer HorizontalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#777777"/>
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical">
<PanelContainer Name="NameBackground" HorizontalExpand="True" VerticalExpand="False">
<RichTextLabel Name="FoodName" HorizontalAlignment="Center"/>
</PanelContainer>
<!-- Frontier: separate recipes from sources -->
<BoxContainer Name="RecipesContainer" HorizontalExpand="True" Visible="false">
<Collapsible HorizontalExpand="True">
<CollapsibleHeading Title="{Loc 'guidebook-food-recipes-header'}"/>
<CollapsibleBody>
<GridContainer Name="RecipesDescriptionContainer"
Margin="10 0 10 0"
Columns="1"
HSeparationOverride="0"
HorizontalAlignment="Stretch"
HorizontalExpand="True"/>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
<!-- End Frontier: separate recipes from sources -->
<BoxContainer Name="SourcesContainer" HorizontalExpand="True" Visible="false"> <!-- Frontier: add Visible -->
<Collapsible HorizontalExpand="True">
<CollapsibleHeading Title="{Loc 'guidebook-food-sources-header'}"/>
<CollapsibleBody>
<GridContainer Name="SourcesDescriptionContainer"
Margin="10 0 10 0"
Columns="1"
HSeparationOverride="5"
HorizontalAlignment="Stretch"
HorizontalExpand="True"/>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
<BoxContainer Name="CompositionContainer" HorizontalExpand="True">
<Collapsible>
<CollapsibleHeading Title="{Loc 'guidebook-food-reagents-header'}"/>
<CollapsibleBody>
<BoxContainer Name="CompositionDescriptionContainer"
Orientation="Vertical"
Margin="10 0 10 0"
HorizontalExpand="True"/>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
<BoxContainer Margin="10 5 10 10" HorizontalExpand="True">
<!-- The troublemaker !-->
<RichTextLabel Name="FoodDescription" HorizontalAlignment="Left"/>
</BoxContainer>
</BoxContainer>
</PanelContainer>
</BoxContainer>
178 changes: 178 additions & 0 deletions Content.Client/_EE/Guidebook/Controls/GuideFoodEmbed.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client._EE.Nutrition.EntitySystems;
using Content.Client.Chemistry.EntitySystems;
using Content.Client.Guidebook.Controls;
using Content.Client.Guidebook.Richtext;
using Content.Client.Message;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

namespace Content.Client._EE.Guidebook.Controls; // Frontier: add _EE

/// <summary>
/// Control for embedding a food recipe into a guidebook.
/// </summary>
[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideFoodEmbed : BoxContainer, IDocumentTag, ISearchableControl
{
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;

private readonly FoodGuideDataSystem _foodGuideData;
private readonly ISawmill _logger = default!;

public GuideFoodEmbed()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_foodGuideData = _systemManager.GetEntitySystem<FoodGuideDataSystem>();
_logger = Logger.GetSawmill("food guide");
MouseFilter = MouseFilterMode.Stop;
}

public GuideFoodEmbed(FoodGuideEntry entry) : this()
{
GenerateControl(entry);
}

public bool CheckMatchesSearch(string query)
{
return FoodName.GetMessage()?.Contains(query, StringComparison.InvariantCultureIgnoreCase) == true
|| FoodDescription.GetMessage()?.Contains(query, StringComparison.InvariantCultureIgnoreCase) == true;
}

public void SetHiddenState(bool state, string query)
{
Visible = CheckMatchesSearch(query) ? state : !state;
}

public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
control = null;
if (!args.TryGetValue("Food", out var id))
{
_logger.Error("Food embed tag is missing food prototype argument.");
return false;
}

if (!_foodGuideData.TryGetData(id, out var data))
{
_logger.Warning($"Specified food prototype \"{id}\" does not have any known sources.");
return false;
}

GenerateControl(data);

control = this;
return true;
}

private void GenerateControl(FoodGuideEntry data)
{
_prototype.TryIndex(data.Result, out var proto);
if (proto == null)
{
FoodName.SetMarkup(Loc.GetString("guidebook-food-unknown-proto", ("id", data.Result)));
return;
}

var composition = data.Composition
.Select(it => _prototype.TryIndex<ReagentPrototype>(it.Reagent.Prototype, out var reagent) ? (reagent, it.Quantity) : (null, 0))
.Where(it => it.reagent is not null)
.Cast<(ReagentPrototype, FixedPoint2)>()
.ToList();

#region Colors

CalculateColors(composition, out var textColor, out var backgroundColor);

NameBackground.PanelOverride = new StyleBoxFlat
{
BackgroundColor = backgroundColor
};
FoodName.SetMarkup(Loc.GetString("guidebook-food-name", ("color", textColor), ("name", proto.Name)));

#endregion

// Frontier: separate Recipes from Sources
#region Recipes
if (data.Recipes.Length > 0)
RecipesContainer.Visible = true;

foreach (var recipe in data.Recipes.OrderBy(it => it.OutputCount))
{
var control = new GuideFoodSource(proto, recipe, _prototype);
RecipesDescriptionContainer.AddChild(control);
}

#endregion
// End Frontier

#region Sources
if (data.Sources.Length > 0) // Frontier
SourcesContainer.Visible = true; // Frontier

foreach (var source in data.Sources.OrderBy(it => it.OutputCount))
{
var control = new GuideFoodSource(proto, source, _prototype);
SourcesDescriptionContainer.AddChild(control);
}

#endregion

#region Composition

foreach (var (reagent, quantity) in composition)
{
var control = new GuideFoodComposition(reagent, quantity);
CompositionDescriptionContainer.AddChild(control);
}

#endregion

FormattedMessage description = new();
description.AddText(proto?.Description ?? string.Empty);
// Cannot describe food flavor or smth beause food is entirely server-side

FoodDescription.SetMessage(description);
}

private void CalculateColors(List<(ReagentPrototype, FixedPoint2)> composition, out Color text, out Color background)
{
// Background color is calculated as the weighted average of the colors of the composition.
// Text color is determined based on background luminosity.
float r = 0, g = 0, b = 0;
FixedPoint2 weight = 0;

foreach (var (proto, quantity) in composition)
{
var tcolor = proto.SubstanceColor;
var prevalence =
quantity <= 0 ? 0f
: weight == 0f ? 1f
: (quantity / (weight + quantity)).Float();

r = r * (1 - prevalence) + tcolor.R * prevalence;
g = g * (1 - prevalence) + tcolor.G * prevalence;
b = b * (1 - prevalence) + tcolor.B * prevalence;

if (quantity > 0)
weight += quantity;
}

// Copied from GuideReagentEmbed which was probably copied from stackoverflow. This is the formula for color luminosity.
var lum = 0.2126f * r + 0.7152f * g + 0.0722f;

background = new Color(r, g, b);
text = lum > 0.5f ? Color.Black : Color.White;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Vertical">
<BoxContainer Name="GroupContainer" Orientation="Vertical"/>
</BoxContainer>
38 changes: 38 additions & 0 deletions Content.Client/_EE/Guidebook/Controls/GuideFoodGroupEmbed.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client._EE.Nutrition.EntitySystems;
using Content.Client.Guidebook.Richtext;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;

namespace Content.Client._EE.Guidebook.Controls; // Frontier: add _EE

[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideFoodGroupEmbed : BoxContainer, IDocumentTag
{
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;

public GuideFoodGroupEmbed()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
MouseFilter = MouseFilterMode.Stop;

foreach (var data in _sysMan.GetEntitySystem<FoodGuideDataSystem>().Registry.OrderBy(it => it.Identifier))
{
var embed = new GuideFoodEmbed(data);
GroupContainer.AddChild(embed);
}
}

public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
control = this;
return true;
}
}
37 changes: 37 additions & 0 deletions Content.Client/_EE/Guidebook/Controls/GuideFoodSource.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal"
HorizontalAlignment="Stretch"
HorizontalExpand="True"
Margin="0 0 0 5">
<BoxContainer Name="ReactantsContainer" Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center"> <!-- Frontier: add HorizontalExpand -->
<TextureRect HorizontalAlignment="Center"
Name="SourceTexture"
Access="Public"/>
<!-- Using rich label here because apparently normal labels do not support soft wrap -->
<RichTextLabel Name="SourceLabel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Access="Public"
Margin="2 0 0 0"/> <!-- Frontier: Label<RichTextLabel -->
</BoxContainer>
<BoxContainer Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center"> <!-- Frontier: remove HorizontalExpand -->
<TextureRect HorizontalAlignment="Center"
Name="ProcessingTexture"
Access="Public"/>
<RichTextLabel Name="ProcessingLabel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Access="Public"
Margin="2 0 0 0"/> <!-- Frontier: Label<RichTextLabel -->
</BoxContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center"> <!-- Frontier: add HorizontalExpand -->
<TextureRect HorizontalAlignment="Center"
Name="OutputsTexture"
Access="Public"/>
<RichTextLabel Name="OutputsLabel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Access="Public"
Margin="2 0 0 0"/> <!-- Frontier: Label<RichTextLabel -->
</BoxContainer>
</BoxContainer>
Loading

0 comments on commit af2bf00

Please sign in to comment.