Merge remote-tracking branch 'upstream/master' into ed-23-02-2025-upstream

# Conflicts:
#	Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs
#	Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs
#	Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
This commit is contained in:
Ed
2025-02-23 23:07:55 +03:00
406 changed files with 70688 additions and 45859 deletions

View File

@@ -0,0 +1,35 @@
<PanelContainer xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Margin="5 5 5 5">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#777777"/>
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical">
<PanelContainer HorizontalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
<BoxContainer Name="IconContainer"/>
<RichTextLabel Name="ResultName"/>
</BoxContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#393c3f"/>
</PanelContainer.PanelOverride>
</PanelContainer>
<GridContainer Columns="2" Margin="10">
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
<Label Text="{Loc 'guidebook-microwave-ingredients-header'}"/>
<GridContainer Columns="3" Name="IngredientsGrid"/>
</BoxContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
<Label Text="{Loc 'guidebook-microwave-cook-time-header'}"/>
<RichTextLabel Name="CookTimeLabel"/>
</BoxContainer>
</GridContainer>
<BoxContainer Margin="10">
<RichTextLabel Name="ResultDescription" HorizontalAlignment="Left"/>
</BoxContainer>
</BoxContainer>
</PanelContainer>

View File

@@ -0,0 +1,183 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.Guidebook.Richtext;
using Content.Client.Message;
using Content.Client.UserInterface.ControlExtensions;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Kitchen;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Guidebook.Controls;
/// <summary>
/// Control for embedding a microwave recipe into a guidebook.
/// </summary>
[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideMicrowaveEmbed : PanelContainer, IDocumentTag, ISearchableControl
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
public GuideMicrowaveEmbed()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
MouseFilter = MouseFilterMode.Stop;
_sawmill = _logManager.GetSawmill("guidemicrowaveembed");
}
public GuideMicrowaveEmbed(string recipe) : this()
{
GenerateControl(_prototype.Index<FoodRecipePrototype>(recipe));
}
public GuideMicrowaveEmbed(FoodRecipePrototype recipe) : this()
{
GenerateControl(recipe);
}
public bool CheckMatchesSearch(string query)
{
return this.ChildrenContainText(query);
}
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("Recipe", out var id))
{
_sawmill.Error("Recipe embed tag is missing recipe prototype argument");
return false;
}
if (!_prototype.TryIndex<FoodRecipePrototype>(id, out var recipe))
{
_sawmill.Error($"Specified recipe prototype \"{id}\" is not a valid recipe prototype");
return false;
}
GenerateControl(recipe);
control = this;
return true;
}
private void GenerateHeader(FoodRecipePrototype recipe)
{
var entity = _prototype.Index<EntityPrototype>(recipe.Result);
IconContainer.AddChild(new GuideEntityEmbed(recipe.Result, false, false));
ResultName.SetMarkup(entity.Name);
ResultDescription.SetMarkup(entity.Description);
}
private void GenerateSolidIngredients(FoodRecipePrototype recipe)
{
foreach (var (product, amount) in recipe.IngredientsSolids.OrderByDescending(p => p.Value))
{
var ingredient = _prototype.Index<EntityPrototype>(product);
IngredientsGrid.AddChild(new GuideEntityEmbed(product, false, false));
// solid name
var solidNameMsg = new FormattedMessage();
solidNameMsg.AddMarkupOrThrow(Loc.GetString("guidebook-microwave-solid-name-display", ("ingredient", ingredient.Name)));
solidNameMsg.Pop();
var solidNameLabel = new RichTextLabel();
solidNameLabel.SetMessage(solidNameMsg);
IngredientsGrid.AddChild(solidNameLabel);
// solid quantity
var solidQuantityMsg = new FormattedMessage();
solidQuantityMsg.AddMarkupOrThrow(Loc.GetString("guidebook-microwave-solid-quantity-display", ("amount", amount)));
solidQuantityMsg.Pop();
var solidQuantityLabel = new RichTextLabel();
solidQuantityLabel.SetMessage(solidQuantityMsg);
IngredientsGrid.AddChild(solidQuantityLabel);
}
}
private void GenerateLiquidIngredients(FoodRecipePrototype recipe)
{
foreach (var (product, amount) in recipe.IngredientsReagents.OrderByDescending(p => p.Value))
{
var reagent = _prototype.Index<ReagentPrototype>(product);
// liquid color
var liquidColorMsg = new FormattedMessage();
liquidColorMsg.AddMarkupOrThrow(Loc.GetString("guidebook-microwave-reagent-color-display", ("color", reagent.SubstanceColor)));
liquidColorMsg.Pop();
var liquidColorLabel = new RichTextLabel();
liquidColorLabel.SetMessage(liquidColorMsg);
liquidColorLabel.HorizontalAlignment = Control.HAlignment.Center;
IngredientsGrid.AddChild(liquidColorLabel);
// liquid name
var liquidNameMsg = new FormattedMessage();
liquidNameMsg.AddMarkupOrThrow(Loc.GetString("guidebook-microwave-reagent-name-display", ("reagent", reagent.LocalizedName)));
liquidNameMsg.Pop();
var liquidNameLabel = new RichTextLabel();
liquidNameLabel.SetMessage(liquidNameMsg);
IngredientsGrid.AddChild(liquidNameLabel);
// liquid quantity
var liquidQuantityMsg = new FormattedMessage();
liquidQuantityMsg.AddMarkupOrThrow(Loc.GetString("guidebook-microwave-reagent-quantity-display", ("amount", amount)));
liquidQuantityMsg.Pop();
var liquidQuantityLabel = new RichTextLabel();
liquidQuantityLabel.SetMessage(liquidQuantityMsg);
IngredientsGrid.AddChild(liquidQuantityLabel);
}
}
private void GenerateIngredients(FoodRecipePrototype recipe)
{
GenerateLiquidIngredients(recipe);
GenerateSolidIngredients(recipe);
}
private void GenerateCookTime(FoodRecipePrototype recipe)
{
var msg = new FormattedMessage();
msg.AddMarkupOrThrow(Loc.GetString("guidebook-microwave-cook-time", ("time", recipe.CookTime)));
msg.Pop();
CookTimeLabel.SetMessage(msg);
}
private void GenerateControl(FoodRecipePrototype recipe)
{
GenerateHeader(recipe);
GenerateIngredients(recipe);
GenerateCookTime(recipe);
}
}

View File

@@ -0,0 +1,59 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.Guidebook.Richtext;
using Content.Shared.Kitchen;
using JetBrains.Annotations;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Guidebook.Controls;
/// <summary>
/// Control for listing microwave recipes in a guidebook
/// </summary>
[UsedImplicitly]
public sealed partial class GuideMicrowaveGroupEmbed : BoxContainer, IDocumentTag
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
public GuideMicrowaveGroupEmbed()
{
Orientation = LayoutOrientation.Vertical;
IoCManager.InjectDependencies(this);
MouseFilter = MouseFilterMode.Stop;
}
public GuideMicrowaveGroupEmbed(string group) : this()
{
CreateEntries(group);
}
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
control = null;
if (!args.TryGetValue("Group", out var group))
{
Logger.Error("Microwave group embed tag is missing group argument");
return false;
}
CreateEntries(group);
control = this;
return true;
}
private void CreateEntries(string group)
{
var prototypes = _prototype.EnumeratePrototypes<FoodRecipePrototype>()
.Where(p => p.Group.Equals(group))
.OrderBy(p => p.Name);
foreach (var recipe in prototypes)
{
var embed = new GuideMicrowaveEmbed(recipe);
AddChild(embed);
}
}
}

View File

@@ -134,24 +134,14 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
HashSet<ProtoId<GuideEntryPrototype>> entries = new(_entries.Keys);
foreach (var entry in _entries.Values)
{
if (entry.Children.Count > 0)
{
var sortedChildren = entry.Children
.Select(childId => _entries[childId])
.OrderBy(childEntry => childEntry.Priority)
.ThenBy(childEntry => Loc.GetString(childEntry.Name))
.Select(childEntry => new ProtoId<GuideEntryPrototype>(childEntry.Id))
.ToList();
entry.Children = sortedChildren;
}
entries.ExceptWith(entry.Children);
}
rootEntries = entries.ToList();
}
// Only roots need to be sorted.
// As defined in the SS14 Dev Wiki, children are already sorted based on their child field order within their parent's prototype definition.
// Roots are sorted by priority. If there is no defined priority for a root then it is by definition sorted undefined.
return rootEntries
.Select(rootEntryId => _entries[rootEntryId])
.OrderBy(rootEntry => rootEntry.Priority)