diff --git a/Content.Client/SS220/SupaKitchen/Components/CookingConstantlyComponent.cs b/Content.Client/SS220/SupaKitchen/Components/CookingConstantlyComponent.cs new file mode 100644 index 00000000000000..96ab6f87376fef --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/Components/CookingConstantlyComponent.cs @@ -0,0 +1,13 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Shared.SS220.SupaKitchen.Components; + +namespace Content.Client.SS220.SupaKitchen.Components; + +[RegisterComponent] +public sealed partial class CookingConstantlyComponent : SharedCookingConstantlyComponent +{ + [DataField] + public string ActiveState = "oven_on"; + [DataField] + public string NonActiveState = "oven_off"; +} diff --git a/Content.Client/SS220/SupaKitchen/CookingMachineBoundUserInterface.cs b/Content.Client/SS220/SupaKitchen/CookingMachineBoundUserInterface.cs new file mode 100644 index 00000000000000..f9cb9674b3b28c --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/CookingMachineBoundUserInterface.cs @@ -0,0 +1,126 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Shared.Chemistry.Reagent; +using Content.Shared.SS220.SupaKitchen; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.SS220.SupaKitchen.UI +{ + [UsedImplicitly] + public sealed class CookingMachineBoundUserInterface : BoundUserInterface + { + [ViewVariables] + private CookingMachineWindow? _menu; + + [ViewVariables] + private readonly Dictionary _solids = new(); + + [ViewVariables] + private readonly Dictionary _reagents = new(); + + private IEntityManager _entManager; + + public CookingMachineBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _entManager = IoCManager.Resolve(); + } + + protected override void Open() + { + base.Open(); + _menu = new CookingMachineWindow(this); + _menu.OpenCentered(); + _menu.OnClose += Close; + _menu.StartButton.OnPressed += _ => SendMessage(new CookingMachineStartCookMessage()); + _menu.EjectButton.OnPressed += _ => SendMessage(new CookingMachineEjectMessage()); + _menu.IngredientsList.OnItemSelected += args => + { + SendMessage(new CookingMachineEjectSolidIndexedMessage(_entManager.GetNetEntity(_solids[args.ItemIndex]))); + }; + + _menu.OnCookTimeSelected += (args, buttonIndex) => + { + var actualButton = (CookingMachineWindow.MicrowaveCookTimeButton) args.Button; + SendMessage(new CookingMachineSelectCookTimeMessage(buttonIndex, actualButton.CookTime)); + }; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (!disposing) + return; + + _solids.Clear(); + _menu?.Dispose(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + if (state is not CookingMachineUpdateUserInterfaceState cState) + { + return; + } + + if (cState.MachineState is CookingMachineState.Idle) + _menu?.ToggleBusyDisableOverlayPanel(false); + else + _menu?.ToggleBusyDisableOverlayPanel(true); + + RefreshContentsDisplay(_entManager.GetEntityArray(cState.ContainedSolids)); + + if (_menu == null) return; + + var currentlySelectedTimeButton = (Button) _menu.CookTimeButtonVbox.GetChild(cState.ActiveButtonIndex); + currentlySelectedTimeButton.Pressed = true; + var cookTime = cState.ActiveButtonIndex == 0 + ? Loc.GetString("cooking-machine-menu-instant-button") + : cState.CurrentCookTime.ToString(); + _menu.CookTimeInfoLabel.Text = Loc.GetString("cooking-machine-bound-user-interface-cook-time-label", + ("time", cookTime)); + + _menu.EjectButton.Visible = !cState.EjectUnavailable; + } + + private void RefreshContentsDisplay(EntityUid[] containedSolids) + { + _reagents.Clear(); + + if (_menu == null) return; + + _solids.Clear(); + _menu.IngredientsList.Clear(); + foreach (var entity in containedSolids) + { + if (EntMan.Deleted(entity)) + { + return; + } + + // TODO just use sprite view + + Texture? texture; + if (EntMan.TryGetComponent(entity, out var iconComponent)) + { + texture = EntMan.System().GetIcon(iconComponent); + } + else if (EntMan.TryGetComponent(entity, out var spriteComponent)) + { + texture = spriteComponent.Icon?.Default; + } + else + { + continue; + } + + var solidItem = _menu.IngredientsList.AddItem(EntMan.GetComponent(entity).EntityName, texture); + var solidIndex = _menu.IngredientsList.IndexOf(solidItem); + _solids.Add(solidIndex, entity); + } + } + } +} diff --git a/Content.Client/SS220/SupaKitchen/CookingMachineVIsual.cs b/Content.Client/SS220/SupaKitchen/CookingMachineVIsual.cs new file mode 100644 index 00000000000000..4c9818d3f16f13 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/CookingMachineVIsual.cs @@ -0,0 +1,8 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +namespace Content.Client.SS220.SupaKitchen; + +public enum CookingMachineVisualizerLayers : byte +{ + Base, + BaseUnlit +} diff --git a/Content.Client/SS220/SupaKitchen/Systems/CookingConstantlySystem.cs b/Content.Client/SS220/SupaKitchen/Systems/CookingConstantlySystem.cs new file mode 100644 index 00000000000000..efa5be2d55b301 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/Systems/CookingConstantlySystem.cs @@ -0,0 +1,41 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Client.SS220.SupaKitchen.Components; +using Content.Shared.SS220.SupaKitchen.Components; +using Content.Shared.SS220.SupaKitchen.Systems; +using Content.Shared.Storage.Components; +using Robust.Client.GameObjects; +using Robust.Shared.GameStates; + +namespace Content.Client.SS220.SupaKitchen.Systems; + +public sealed partial class CookingConstantlySystem : SharedCookingConstantlySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + + SubscribeLocalEvent(OnStorageOpenAttempt); + SubscribeLocalEvent(OnStorageCloseAttempt); + SubscribeLocalEvent(OnStorageOpen); + + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnAppearanceChange(Entity entity, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + if (!_appearance.TryGetData(entity, CookingConstantlyVisuals.Active, out var isActive)) + return; + + var state = isActive ? entity.Comp.ActiveState : entity.Comp.NonActiveState; + args.Sprite.LayerSetState(CookingConstantlyVisuals.Active, state); + args.Sprite.LayerSetVisible(CookingConstantlyVisuals.ActiveUnshaded, isActive); + } +} diff --git a/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeEmbed.xaml b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeEmbed.xaml new file mode 100644 index 00000000000000..717ab5ec8752a1 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeEmbed.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeEmbed.xaml.cs b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeEmbed.xaml.cs new file mode 100644 index 00000000000000..c4d99de289a468 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeEmbed.xaml.cs @@ -0,0 +1,187 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Content.Client.Guidebook.Controls; +using Content.Client.Guidebook.Richtext; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; +using Content.Shared.SS220.SupaKitchen; +using Content.Client.Message; +using Robust.Shared.Utility; +using Content.Shared.Chemistry.Reagent; +using Robust.Client.GameObjects; +using Content.Shared.FixedPoint; + +namespace Content.Client.SS220.SupaKitchen.UI.Controls; + +/// +/// Control for embedding a reagent into a guidebook. +/// +[UsedImplicitly, GenerateTypedNameReferences] +public sealed partial class GuideCookingRecipeEmbed : BoxContainer, IDocumentTag, ISearchableControl +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IEntitySystemManager _entSysmMan = default!; + + private readonly SpriteSystem _spriteSystem; + + private HashSet _nameSearchCache; + + private readonly ISawmill _sawmill; + + public GuideCookingRecipeEmbed() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + _spriteSystem = _entSysmMan.GetEntitySystem(); + + MouseFilter = MouseFilterMode.Stop; + _sawmill = Logger.GetSawmill("KitchenCookbook"); + + _nameSearchCache = new(); + } + + public GuideCookingRecipeEmbed(CookingRecipePrototype recipe) : this() + { + GenerateControl(recipe); + } + + // uhhh shit i'm not sure about the performance + public bool CheckMatchesSearch(string query) + { + foreach (var match in _nameSearchCache) + { + if (match.Contains(query)) + return true; + } + + return false; + } + + public void SetHiddenState(bool state, string query) + { + this.Visible = CheckMatchesSearch(query) ? state : !state; + } + + public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control) + { + control = null; + if (!args.TryGetValue("Recipe", out var id)) + { + _sawmill.Error("Recipe embed tag is missing reagent prototype argument"); + return false; + } + + if (!_prototype.TryIndex(id, out var recipe)) + { + _sawmill.Error($"Specified CookingRecipe prototype \"{id}\" is not a valid CookingRecipe prototype"); + return false; + } + + GenerateControl(recipe); + + control = this; + return true; + } + + private void GenerateControl(CookingRecipePrototype recipe) + { + if (!_prototype.TryIndex(recipe.Result, out var product)) + return; + + _nameSearchCache.Add(product.Name); + RecipeLabelTitle.SetMarkup(product.Name); + //RecipeLabelTitle.SetMarkup(recipe.Name); + + // reagents + var reagentsMsg = new FormattedMessage(); + var reagentIngredientsCount = recipe.IngredientsReagents.Count; + var u = 0; + var reagentsLabel = new RichTextLabel() + { + HorizontalAlignment = HAlignment.Center, + VerticalAlignment = VAlignment.Center + }; + foreach (var (ingredientId, ingredientAmount) in recipe.IngredientsReagents) + { + if (!_prototype.TryIndex(ingredientId, out var ingredientProto)) + { + reagentIngredientsCount--; + continue; + } + + var ingredientName = ingredientProto.LocalizedName; + _nameSearchCache.Add(ingredientName); + + reagentsMsg.AddMarkupOrThrow(Loc.GetString("guidebook-cooking-recipes-ingredient-display", + ("reagent", ingredientName), ("ratio", ingredientAmount))); + + u++; + if (u < reagentIngredientsCount) + reagentsMsg.PushNewline(); + } + + if (!reagentsMsg.IsEmpty) + { + reagentsMsg.Pop(); + reagentsLabel.SetMessage(reagentsMsg); + IngredientsContainer.AddChild(reagentsLabel); + } + + // solid ingredients + foreach (var (ingredientId, ingredientAmount) in recipe.IngredientsSolids) + { + if (!_prototype.TryIndex(ingredientId, out var ingredientProto)) + continue; + + var ingredientName = ingredientProto.Name; + _nameSearchCache.Add(ingredientName); + + IngredientsContainer.AddChild(GetEntContainer(ingredientProto, ingredientAmount)); + } + + // output + ProductsContainer.AddChild(GetEntContainer(product, 1)); + + if (!_prototype.TryIndex(recipe.InstrumentType, out var instrumentProto)) + return; + + var instrumentMsg = new FormattedMessage(); + instrumentMsg.AddMarkupOrThrow(instrumentProto.Name); + instrumentMsg.PushNewline(); + instrumentMsg.AddMarkupOrThrow(Loc.GetString("guidebook-cooking-recipes-timer-display", ("time", recipe.CookTime))); + instrumentMsg.Pop(); + InstrumentName.SetMessage(instrumentMsg); + + if (instrumentProto.IconPath is not null) + InstrumentIcon.TexturePath = instrumentProto.IconPath; + } + + private BoxContainer GetEntContainer(EntityPrototype prototype, FixedPoint2 amount) + { + var entContainer = new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + HorizontalExpand = true, + HorizontalAlignment = HAlignment.Center, + }; + + var entView = new EntityPrototypeView(); + entView.SetPrototype(prototype); + entContainer.AddChild(entView); + + var entMsg = new FormattedMessage(); + entMsg.AddMarkupOrThrow(Loc.GetString("guidebook-cooking-recipes-ingredient-display", + ("reagent", prototype.Name), ("ratio", amount))); + entMsg.Pop(); + + var entLabel = new RichTextLabel(); + entLabel.SetMessage(entMsg); + entContainer.AddChild(entLabel); + + return entContainer; + } +} diff --git a/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeGroupEmbed.xaml b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeGroupEmbed.xaml new file mode 100644 index 00000000000000..a3fa4b2360fd63 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeGroupEmbed.xaml @@ -0,0 +1,5 @@ + + + + diff --git a/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeGroupEmbed.xaml.cs b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeGroupEmbed.xaml.cs new file mode 100644 index 00000000000000..10bcbd4fdd6e84 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/UI/Controls/GuideCookingRecipeGroupEmbed.xaml.cs @@ -0,0 +1,62 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Client.Guidebook.Richtext; +using Content.Shared.SS220.SupaKitchen; +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.SS220.SupaKitchen.UI.Controls; + +/// +/// Control for embedding a group of recipes into a guidebook. +/// +[UsedImplicitly, GenerateTypedNameReferences] +public sealed partial class GuideCookingRecipeGroupEmbed : BoxContainer, IDocumentTag +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public GuideCookingRecipeGroupEmbed() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + MouseFilter = MouseFilterMode.Stop; + } + + public GuideCookingRecipeGroupEmbed(string? recipeGroup, ProtoId? instrumentType) : this() + { + AddCookingRecipes(recipeGroup, instrumentType); + } + + public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control) + { + args.TryGetValue("RecipeGroup", out var recipeGroup); + args.TryGetValue("InstrumentType", out var instrumentType); + + AddCookingRecipes(recipeGroup, instrumentType); + + control = this; + return true; + } + + private void AddCookingRecipes(string? recipeGroup, ProtoId? instrumentType) + { + var prototypes = _prototype.EnumeratePrototypes().Where(r => !r.SecretRecipe); + + if (recipeGroup != null) + prototypes = prototypes.Where(r => r.RecipeGroup == recipeGroup); + + if (instrumentType != null) + prototypes = prototypes.Where(r => r.InstrumentType == instrumentType); + + foreach (var recipe in prototypes) + { + var embed = new GuideCookingRecipeEmbed(recipe); + GroupContainer.AddChild(embed); + } + } +} diff --git a/Content.Client/SS220/SupaKitchen/UI/CookingMachineWindow.xaml b/Content.Client/SS220/SupaKitchen/UI/CookingMachineWindow.xaml new file mode 100644 index 00000000000000..18c666c8069698 --- /dev/null +++ b/Content.Client/SS220/SupaKitchen/UI/CookingMachineWindow.xaml @@ -0,0 +1,74 @@ + + + + + + + + +