diff --git a/Content.Client/Construction/ConstructionPlacementHijack.cs b/Content.Client/Construction/ConstructionPlacementHijack.cs index dc5d7bf342f..e6a8e0f1f00 100644 --- a/Content.Client/Construction/ConstructionPlacementHijack.cs +++ b/Content.Client/Construction/ConstructionPlacementHijack.cs @@ -1,8 +1,10 @@ using System.Linq; using Content.Shared.Construction.Prototypes; +using Robust.Client.GameObjects; using Robust.Client.Placement; -using Robust.Client.Utility; +using Robust.Client.ResourceManagement; using Robust.Shared.Map; +using Robust.Shared.Prototypes; namespace Content.Client.Construction { @@ -45,7 +47,14 @@ namespace Content.Client.Construction public override void StartHijack(PlacementManager manager) { base.StartHijack(manager); - manager.CurrentTextures = _prototype?.Layers.Select(sprite => sprite.DirFrame0()).ToList(); + + if (_prototype is null || !_constructionSystem.TryGetRecipePrototype(_prototype.ID, out var targetProtoId)) + return; + + if (!IoCManager.Resolve().TryIndex(targetProtoId, out EntityPrototype? proto)) + return; + + manager.CurrentTextures = SpriteComponent.GetPrototypeTextures(proto, IoCManager.Resolve()).ToList(); } } } diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 17d6b053883..77dfc9d8056 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -1,11 +1,10 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Client.Popups; using Content.Shared.Construction; using Content.Shared.Construction.Prototypes; -using Content.Shared.Construction.Steps; using Content.Shared.Examine; using Content.Shared.Input; -using Content.Shared.Interaction; using Content.Shared.Wall; using JetBrains.Annotations; using Robust.Client.GameObjects; @@ -15,6 +14,7 @@ using Robust.Shared.Input.Binding; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Client.Construction { @@ -25,7 +25,6 @@ namespace Content.Client.Construction public sealed class ConstructionSystem : SharedConstructionSystem { [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ExamineSystemShared _examineSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -33,6 +32,8 @@ namespace Content.Client.Construction private readonly Dictionary _ghosts = new(); private readonly Dictionary _guideCache = new(); + private readonly Dictionary _recipesMetadataCache = []; + public bool CraftingEnabled { get; private set; } /// @@ -40,6 +41,8 @@ namespace Content.Client.Construction { base.Initialize(); + WarmupRecipesCache(); + UpdatesOutsidePrediction = true; SubscribeLocalEvent(HandlePlayerAttached); SubscribeNetworkEvent(HandleAckStructure); @@ -63,6 +66,77 @@ namespace Content.Client.Construction ClearGhost(component.GhostId); } + public bool TryGetRecipePrototype(string constructionProtoId, [NotNullWhen(true)] out string? targetProtoId) + { + if (_recipesMetadataCache.TryGetValue(constructionProtoId, out targetProtoId)) + return true; + + targetProtoId = null; + return false; + } + + private void WarmupRecipesCache() + { + foreach (var constructionProto in PrototypeManager.EnumeratePrototypes()) + { + if (!PrototypeManager.TryIndex(constructionProto.Graph, out var graphProto)) + continue; + + if (constructionProto.TargetNode is not { } targetNodeId) + continue; + + if (!graphProto.Nodes.TryGetValue(targetNodeId, out var targetNode)) + continue; + + // Recursion is for wimps. + var stack = new Stack(); + stack.Push(targetNode); + + do + { + var node = stack.Pop(); + + // I never realized if this uid affects anything... + // EntityUid? userUid = args.SenderSession.State.ControlledEntity.HasValue + // ? GetEntity(args.SenderSession.State.ControlledEntity.Value) + // : null; + + // We try to get the id of the target prototype, if it fails, we try going through the edges. + if (node.Entity.GetId(null, null, new(EntityManager)) is not { } entityId) + { + // If the stack is not empty, there is a high probability that the loop will go to infinity. + if (stack.Count == 0) + { + foreach (var edge in node.Edges) + { + if (graphProto.Nodes.TryGetValue(edge.Target, out var graphNode)) + stack.Push(graphNode); + } + } + + continue; + } + + // If we got the id of the prototype, we exit the “recursion” by clearing the stack. + stack.Clear(); + + if (!PrototypeManager.TryIndex(constructionProto.ID, out ConstructionPrototype? recipe)) + continue; + + if (!PrototypeManager.TryIndex(entityId, out var proto)) + continue; + + var name = recipe.SetName.HasValue ? Loc.GetString(recipe.SetName) : proto.Name; + var desc = recipe.SetDescription.HasValue ? Loc.GetString(recipe.SetDescription) : proto.Description; + + recipe.Name = name; + recipe.Description = desc; + + _recipesMetadataCache.Add(constructionProto.ID, entityId); + } while (stack.Count > 0); + } + } + private void OnConstructionGuideReceived(ResponseConstructionGuide ev) { _guideCache[ev.ConstructionId] = ev.Guide; @@ -88,7 +162,7 @@ namespace Content.Client.Construction private void HandleConstructionGhostExamined(EntityUid uid, ConstructionGhostComponent component, ExaminedEvent args) { - if (component.Prototype == null) + if (component.Prototype?.Name is null) return; using (args.PushGroup(nameof(ConstructionGhostComponent))) @@ -97,7 +171,7 @@ namespace Content.Client.Construction "construction-ghost-examine-message", ("name", component.Prototype.Name))); - if (!_prototypeManager.TryIndex(component.Prototype.Graph, out ConstructionGraphPrototype? graph)) + if (!PrototypeManager.TryIndex(component.Prototype.Graph, out var graph)) return; var startNode = graph.Nodes[component.Prototype.StartNode]; @@ -198,6 +272,9 @@ namespace Content.Client.Construction return false; } + if (!TryGetRecipePrototype(prototype.ID, out var targetProtoId) || !PrototypeManager.TryIndex(targetProtoId, out EntityPrototype? targetProto)) + return false; + if (GhostPresent(loc)) return false; @@ -214,16 +291,43 @@ namespace Content.Client.Construction comp.GhostId = ghost.GetHashCode(); EntityManager.GetComponent(ghost.Value).LocalRotation = dir.ToAngle(); _ghosts.Add(comp.GhostId, ghost.Value); + var sprite = EntityManager.GetComponent(ghost.Value); sprite.Color = new Color(48, 255, 48, 128); - for (int i = 0; i < prototype.Layers.Count; i++) + if (targetProto.TryGetComponent(out IconComponent? icon, EntityManager.ComponentFactory)) { - sprite.AddBlankLayer(i); // There is no way to actually check if this already exists, so we blindly insert a new one - sprite.LayerSetSprite(i, prototype.Layers[i]); - sprite.LayerSetShader(i, "unshaded"); - sprite.LayerSetVisible(i, true); + sprite.AddBlankLayer(0); + sprite.LayerSetSprite(0, icon.Icon); + sprite.LayerSetShader(0, "unshaded"); + sprite.LayerSetVisible(0, true); } + else if (targetProto.Components.TryGetValue("Sprite", out _)) + { + var dummy = EntityManager.SpawnEntity(targetProtoId, MapCoordinates.Nullspace); + var targetSprite = EntityManager.EnsureComponent(dummy); + EntityManager.System().OnChangeData(dummy, targetSprite); + + for (var i = 0; i < targetSprite.AllLayers.Count(); i++) + { + if (!targetSprite[i].Visible || !targetSprite[i].RsiState.IsValid) + continue; + + var rsi = targetSprite[i].Rsi ?? targetSprite.BaseRSI; + if (rsi is null || !rsi.TryGetState(targetSprite[i].RsiState, out var state) || + state.StateId.Name is null) + continue; + + sprite.AddBlankLayer(i); + sprite.LayerSetSprite(i, new SpriteSpecifier.Rsi(rsi.Path, state.StateId.Name)); + sprite.LayerSetShader(i, "unshaded"); + sprite.LayerSetVisible(i, true); + } + + EntityManager.DeleteEntity(dummy); + } + else + return false; if (prototype.CanBuildInImpassable) EnsureComp(ghost.Value).Arc = new(Math.Tau); diff --git a/Content.Client/Construction/UI/ConstructionMenu.xaml b/Content.Client/Construction/UI/ConstructionMenu.xaml index a934967a533..4020ca43326 100644 --- a/Content.Client/Construction/UI/ConstructionMenu.xaml +++ b/Content.Client/Construction/UI/ConstructionMenu.xaml @@ -1,11 +1,12 @@ - + - + - + @@ -18,7 +19,7 @@ - + diff --git a/Content.Client/Construction/UI/ConstructionMenu.xaml.cs b/Content.Client/Construction/UI/ConstructionMenu.xaml.cs index 9ab8a156005..d94150d5cd9 100644 --- a/Content.Client/Construction/UI/ConstructionMenu.xaml.cs +++ b/Content.Client/Construction/UI/ConstructionMenu.xaml.cs @@ -1,14 +1,11 @@ -using System; using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.Construction.Prototypes; using Robust.Client.AutoGenerated; -using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Graphics; -using Robust.Shared.IoC; -using Robust.Shared.Localization; - +using Robust.Shared.Prototypes; namespace Content.Client.Construction.UI { @@ -28,7 +25,7 @@ namespace Content.Client.Construction.UI bool GridViewButtonPressed { get; set; } bool BuildButtonPressed { get; set; } - ItemList Recipes { get; } + ListContainer Recipes { get; } ItemList RecipeStepList { get; } @@ -36,14 +33,14 @@ namespace Content.Client.Construction.UI GridContainer RecipesGrid { get; } event EventHandler<(string search, string catagory)> PopulateRecipes; - event EventHandler RecipeSelected; + event EventHandler RecipeSelected; event EventHandler RecipeFavorited; event EventHandler BuildButtonToggled; event EventHandler EraseButtonToggled; event EventHandler ClearAllGhosts; void ClearRecipeInfo(); - void SetRecipeInfo(string name, string description, Texture iconTexture, bool isItem, bool isFavorite); + void SetRecipeInfo(string name, string description, EntityPrototype? targetPrototype, bool isItem, bool isFavorite); void ResetPlacement(); #region Window Control @@ -94,8 +91,36 @@ namespace Content.Client.Construction.UI Title = Loc.GetString("construction-menu-title"); BuildButton.Text = Loc.GetString("construction-menu-place-ghost"); - Recipes.OnItemSelected += obj => RecipeSelected?.Invoke(this, obj.ItemList[obj.ItemIndex]); - Recipes.OnItemDeselected += _ => RecipeSelected?.Invoke(this, null); + Recipes.ItemPressed += (_, data) => RecipeSelected?.Invoke(this, data as ConstructionMenuListData); + Recipes.NoItemSelected += () => RecipeSelected?.Invoke(this, null); + Recipes.GenerateItem += (data, button) => + { + if (data is not ConstructionMenuListData (var prototype, var targetPrototype)) + return; + + var entProtoView = new EntityPrototypeView() + { + SetSize = new(32f), + Stretch = SpriteView.StretchMode.Fill, + Scale = new(2), + Margin = new(0, 2), + }; + entProtoView.SetPrototype(targetPrototype); + + var label = new Label() + { + Text = prototype.Name, + Margin = new(5, 0), + }; + + var box = new BoxContainer(); + box.AddChild(entProtoView); + box.AddChild(label); + + button.AddChild(box); + button.ToolTip = prototype.Description; + button.AddStyleClass(ListContainer.StyleClassListContainerButton); + }; SearchBar.OnTextChanged += _ => PopulateRecipes?.Invoke(this, (SearchBar.Text, Categories[OptionCategories.SelectedId])); @@ -121,7 +146,7 @@ namespace Content.Client.Construction.UI public event EventHandler? ClearAllGhosts; public event EventHandler<(string search, string catagory)>? PopulateRecipes; - public event EventHandler? RecipeSelected; + public event EventHandler? RecipeSelected; public event EventHandler? RecipeFavorited; public event EventHandler? BuildButtonToggled; public event EventHandler? EraseButtonToggled; @@ -133,13 +158,17 @@ namespace Content.Client.Construction.UI } public void SetRecipeInfo( - string name, string description, Texture iconTexture, bool isItem, bool isFavorite) + string name, + string description, + EntityPrototype? targetPrototype, + bool isItem, + bool isFavorite) { BuildButton.Disabled = false; BuildButton.Text = Loc.GetString(isItem ? "construction-menu-place-ghost" : "construction-menu-craft"); TargetName.SetMessage(name); TargetDesc.SetMessage(description); - TargetTexture.Texture = iconTexture; + TargetTexture.SetPrototype(targetPrototype?.ID); FavoriteButton.Visible = true; FavoriteButton.Text = Loc.GetString( isFavorite ? "construction-add-favorite-button" : "construction-remove-from-favorite-button"); @@ -150,9 +179,11 @@ namespace Content.Client.Construction.UI BuildButton.Disabled = true; TargetName.SetMessage(string.Empty); TargetDesc.SetMessage(string.Empty); - TargetTexture.Texture = null; + TargetTexture.SetPrototype(null); FavoriteButton.Visible = false; RecipeStepList.Clear(); } + + public sealed record ConstructionMenuListData(ConstructionPrototype Prototype, EntityPrototype TargetPrototype) : ListData; } } diff --git a/Content.Client/Construction/UI/ConstructionMenuPresenter.cs b/Content.Client/Construction/UI/ConstructionMenuPresenter.cs index d35e8fbe769..77f04ee781b 100644 --- a/Content.Client/Construction/UI/ConstructionMenuPresenter.cs +++ b/Content.Client/Construction/UI/ConstructionMenuPresenter.cs @@ -10,10 +10,8 @@ using Robust.Client.Placement; using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; -using Robust.Client.Utility; using Robust.Shared.Enums; using Robust.Shared.Prototypes; -using static Robust.Client.UserInterface.Controls.BaseButton; namespace Content.Client.Construction.UI { @@ -30,18 +28,20 @@ namespace Content.Client.Construction.UI [Dependency] private readonly IPlacementManager _placementManager = default!; [Dependency] private readonly IUserInterfaceManager _uiManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + private readonly SpriteSystem _spriteSystem; private readonly IConstructionMenuView _constructionView; private readonly EntityWhitelistSystem _whitelistSystem; - private readonly SpriteSystem _spriteSystem; private ConstructionSystem? _constructionSystem; private ConstructionPrototype? _selected; private List _favoritedRecipes = []; - private Dictionary _recipeButtons = new(); + private Dictionary _recipeButtons = new(); private string _selectedCategory = string.Empty; - private string _favoriteCatName = "construction-category-favorites"; - private string _forAllCategoryName = "construction-category-all"; + + private const string FavoriteCatName = "construction-category-favorites"; + private const string ForAllCategoryName = "construction-category-all"; + private bool CraftingAvailable { get => _uiManager.GetActiveUIWidget().CraftingButton.Visible; @@ -98,15 +98,18 @@ namespace Content.Client.Construction.UI _placementManager.PlacementChanged += OnPlacementChanged; - _constructionView.OnClose += () => _uiManager.GetActiveUIWidget().CraftingButton.Pressed = false; + _constructionView.OnClose += + () => _uiManager.GetActiveUIWidget().CraftingButton.Pressed = false; _constructionView.ClearAllGhosts += (_, _) => _constructionSystem?.ClearAllGhosts(); _constructionView.PopulateRecipes += OnViewPopulateRecipes; _constructionView.RecipeSelected += OnViewRecipeSelected; _constructionView.BuildButtonToggled += (_, b) => BuildButtonToggled(b); _constructionView.EraseButtonToggled += (_, b) => { - if (_constructionSystem is null) return; - if (b) _placementManager.Clear(); + if (_constructionSystem is null) + return; + if (b) + _placementManager.Clear(); _placementManager.ToggleEraserHijacked(new ConstructionPlacementHijack(_constructionSystem, null)); _constructionView.EraseButtonPressed = b; }; @@ -117,7 +120,7 @@ namespace Content.Client.Construction.UI OnViewPopulateRecipes(_constructionView, (string.Empty, string.Empty)); } - public void OnHudCraftingButtonToggled(ButtonToggledEventArgs args) + public void OnHudCraftingButtonToggled(BaseButton.ButtonToggledEventArgs args) { WindowOpen = args.Pressed; } @@ -139,7 +142,7 @@ namespace Content.Client.Construction.UI _constructionView.ResetPlacement(); } - private void OnViewRecipeSelected(object? sender, ItemList.Item? item) + private void OnViewRecipeSelected(object? sender, ConstructionMenu.ConstructionMenuListData? item) { if (item is null) { @@ -148,12 +151,15 @@ namespace Content.Client.Construction.UI return; } - _selected = (ConstructionPrototype) item.Metadata!; - if (_placementManager.IsActive && !_placementManager.Eraser) UpdateGhostPlacement(); + _selected = item.Prototype; + + if (_placementManager is { IsActive: true, Eraser: false }) + UpdateGhostPlacement(); + PopulateInfo(_selected); } - private void OnGridViewRecipeSelected(object? sender, ConstructionPrototype? recipe) + private void OnGridViewRecipeSelected(object? _, ConstructionPrototype? recipe) { if (recipe is null) { @@ -163,62 +169,21 @@ namespace Content.Client.Construction.UI } _selected = recipe; - if (_placementManager.IsActive && !_placementManager.Eraser) UpdateGhostPlacement(); + + if (_placementManager is { IsActive: true, Eraser: false }) + UpdateGhostPlacement(); + PopulateInfo(_selected); } private void OnViewPopulateRecipes(object? sender, (string search, string catagory) args) { - var (search, category) = args; + if (_constructionSystem is null) + return; - var recipes = new List(); - - var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName; - - if (isEmptyCategory) - _selectedCategory = string.Empty; - else - _selectedCategory = category; - - foreach (var recipe in _prototypeManager.EnumeratePrototypes()) - { - if (recipe.Hide) - continue; - - if (_playerManager.LocalSession == null - || _playerManager.LocalEntity == null - || _whitelistSystem.IsWhitelistFail(recipe.EntityWhitelist, _playerManager.LocalEntity.Value)) - continue; - - if (!string.IsNullOrEmpty(search)) - { - if (!recipe.Name.ToLowerInvariant().Contains(search.Trim().ToLowerInvariant())) - continue; - } - - if (!isEmptyCategory) - { - if (category == _favoriteCatName) - { - if (!_favoritedRecipes.Contains(recipe)) - { - continue; - } - } - else if (recipe.Category != category) - { - continue; - } - } - - recipes.Add(recipe); - } - - recipes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCulture)); + var actualRecipes = GetAndSortRecipes(args); var recipesList = _constructionView.Recipes; - recipesList.Clear(); - var recipesGrid = _constructionView.RecipesGrid; recipesGrid.RemoveAllChildren(); @@ -227,60 +192,120 @@ namespace Content.Client.Construction.UI if (_constructionView.GridViewButtonPressed) { - foreach (var recipe in recipes) - { - var itemButton = new TextureButton - { - TextureNormal = _spriteSystem.Frame0(recipe.Icon), - VerticalAlignment = Control.VAlignment.Center, - Name = recipe.Name, - ToolTip = recipe.Name, - Scale = new Vector2(1.35f), - ToggleMode = true, - }; - var itemButtonPanelContainer = new PanelContainer - { - PanelOverride = new StyleBoxFlat { BackgroundColor = StyleNano.ButtonColorDefault }, - Children = { itemButton }, - }; - - itemButton.OnToggled += buttonToggledEventArgs => - { - SelectGridButton(itemButton, buttonToggledEventArgs.Pressed); - - if (buttonToggledEventArgs.Pressed && - _selected != null && - _recipeButtons.TryGetValue(_selected.Name, out var oldButton)) - { - oldButton.Pressed = false; - SelectGridButton(oldButton, false); - } - - OnGridViewRecipeSelected(this, buttonToggledEventArgs.Pressed ? recipe : null); - }; - - recipesGrid.AddChild(itemButtonPanelContainer); - _recipeButtons[recipe.Name] = itemButton; - var isCurrentButtonSelected = _selected == recipe; - itemButton.Pressed = isCurrentButtonSelected; - SelectGridButton(itemButton, isCurrentButtonSelected); - } + recipesList.PopulateList([]); + PopulateGrid(recipesGrid, actualRecipes); } else { - foreach (var recipe in recipes) - { - recipesList.Add(GetItem(recipe, recipesList)); - } + recipesList.PopulateList(actualRecipes); } } - private void SelectGridButton(TextureButton button, bool select) + private void PopulateGrid(GridContainer recipesGrid, + IEnumerable actualRecipes) + { + foreach (var recipe in actualRecipes) + { + var protoView = new EntityPrototypeView() + { + Scale = new Vector2(1.2f), + }; + protoView.SetPrototype(recipe.TargetPrototype); + + var itemButton = new ContainerButton() + { + VerticalAlignment = Control.VAlignment.Center, + Name = recipe.TargetPrototype.Name, + ToolTip = recipe.TargetPrototype.Name, + ToggleMode = true, + Children = { protoView }, + }; + + var itemButtonPanelContainer = new PanelContainer + { + PanelOverride = new StyleBoxFlat { BackgroundColor = StyleNano.ButtonColorDefault }, + Children = { itemButton }, + }; + + itemButton.OnToggled += buttonToggledEventArgs => + { + SelectGridButton(itemButton, buttonToggledEventArgs.Pressed); + + if (buttonToggledEventArgs.Pressed && + _selected != null && + _recipeButtons.TryGetValue(_selected.Name!, out var oldButton)) + { + oldButton.Pressed = false; + SelectGridButton(oldButton, false); + } + + OnGridViewRecipeSelected(this, buttonToggledEventArgs.Pressed ? recipe.Prototype : null); + }; + + recipesGrid.AddChild(itemButtonPanelContainer); + _recipeButtons[recipe.Prototype.Name!] = itemButton; + var isCurrentButtonSelected = _selected == recipe.Prototype; + itemButton.Pressed = isCurrentButtonSelected; + SelectGridButton(itemButton, isCurrentButtonSelected); + } + } + + private List GetAndSortRecipes((string, string) args) + { + var recipes = new List(); + + var (search, category) = args; + var isEmptyCategory = string.IsNullOrEmpty(category) || category == ForAllCategoryName; + _selectedCategory = isEmptyCategory ? string.Empty : category; + + foreach (var recipe in _prototypeManager.EnumeratePrototypes()) + { + if (recipe.Hide) + continue; + + if (_playerManager.LocalSession == null + || _playerManager.LocalEntity == null + || _whitelistSystem.IsWhitelistFail(recipe.EntityWhitelist, _playerManager.LocalEntity.Value)) + continue; + + if (!string.IsNullOrEmpty(search) && (recipe.Name is { } name && + !name.Contains(search.Trim(), + StringComparison.InvariantCultureIgnoreCase))) + continue; + + if (!isEmptyCategory) + { + if ((category != FavoriteCatName || !_favoritedRecipes.Contains(recipe)) && + recipe.Category != category) + continue; + } + + if (!_constructionSystem!.TryGetRecipePrototype(recipe.ID, out var targetProtoId)) + { + Logger.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.", + recipe.ID, + nameof(ConstructionPrototype)); + continue; + } + + if (!_prototypeManager.TryIndex(targetProtoId, out EntityPrototype? proto)) + continue; + + recipes.Add(new(recipe, proto)); + } + + recipes.Sort( + (a, b) => string.Compare(a.Prototype.Name, b.Prototype.Name, StringComparison.InvariantCulture)); + + return recipes; + } + + private void SelectGridButton(BaseButton button, bool select) { if (button.Parent is not PanelContainer buttonPanel) return; - button.Modulate = select ? Color.Green : Color.White; + button.Modulate = select ? Color.Green : Color.Transparent; var buttonColor = select ? StyleNano.ButtonColorDefault : Color.Transparent; buttonPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = buttonColor }; } @@ -302,12 +327,12 @@ namespace Content.Client.Construction.UI // hard-coded to show all recipes var idx = 0; - categoriesArray[idx++] = _forAllCategoryName; + categoriesArray[idx++] = ForAllCategoryName; // hard-coded to show favorites if it need if (isFavorites) { - categoriesArray[idx++] = _favoriteCatName; + categoriesArray[idx++] = FavoriteCatName; } var sortedProtoCategories = uniqueCategories.OrderBy(Loc.GetString); @@ -325,18 +350,31 @@ namespace Content.Client.Construction.UI if (!string.IsNullOrEmpty(selectCategory) && selectCategory == categoriesArray[i]) _constructionView.OptionCategories.SelectId(i); - } _constructionView.Categories = categoriesArray; } - private void PopulateInfo(ConstructionPrototype prototype) + private void PopulateInfo(ConstructionPrototype? prototype) { + if (_constructionSystem is null) + return; + _constructionView.ClearRecipeInfo(); + if (prototype is null) + return; + + if (!_constructionSystem.TryGetRecipePrototype(prototype.ID, out var targetProtoId)) + return; + + if (!_prototypeManager.TryIndex(targetProtoId, out EntityPrototype? proto)) + return; + _constructionView.SetRecipeInfo( - prototype.Name, prototype.Description, _spriteSystem.Frame0(prototype.Icon), + prototype.Name!, + prototype.Description!, + proto, prototype.Type != ConstructionType.Item, !_favoritedRecipes.Contains(prototype)); @@ -349,16 +387,17 @@ namespace Content.Client.Construction.UI if (_constructionSystem?.GetGuide(prototype) is not { } guide) return; - foreach (var entry in guide.Entries) { var text = entry.Arguments != null - ? Loc.GetString(entry.Localization, entry.Arguments) : Loc.GetString(entry.Localization); + ? Loc.GetString(entry.Localization, entry.Arguments) + : Loc.GetString(entry.Localization); if (entry.EntryNumber is { } number) { text = Loc.GetString("construction-presenter-step-wrapper", - ("step-number", number), ("text", text)); + ("step-number", number), + ("text", text)); } // The padding needs to be applied regardless of text length... (See PadLeft documentation) @@ -369,23 +408,12 @@ namespace Content.Client.Construction.UI } } - private ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList) - { - return new(itemList) - { - Metadata = recipe, - Text = recipe.Name, - Icon = _spriteSystem.Frame0(recipe.Icon), - TooltipEnabled = true, - TooltipText = recipe.Description, - }; - } - private void BuildButtonToggled(bool pressed) { if (pressed) { - if (_selected == null) return; + if (_selected == null) + return; // not bound to a construction system if (_constructionSystem is null) @@ -402,10 +430,11 @@ namespace Content.Client.Construction.UI } _placementManager.BeginPlacing(new PlacementInformation - { - IsTile = false, - PlacementOption = _selected.PlacementMode - }, new ConstructionPlacementHijack(_constructionSystem, _selected)); + { + IsTile = false, + PlacementOption = _selected.PlacementMode + }, + new ConstructionPlacementHijack(_constructionSystem, _selected)); UpdateGhostPlacement(); } @@ -429,38 +458,39 @@ namespace Content.Client.Construction.UI var constructSystem = _systemManager.GetEntitySystem(); _placementManager.BeginPlacing(new PlacementInformation() - { - IsTile = false, - PlacementOption = _selected.PlacementMode, - }, new ConstructionPlacementHijack(constructSystem, _selected)); + { + IsTile = false, + PlacementOption = _selected.PlacementMode, + }, + new ConstructionPlacementHijack(constructSystem, _selected)); _constructionView.BuildButtonPressed = true; } private void OnSystemLoaded(object? sender, SystemChangedArgs args) { - if (args.System is ConstructionSystem system) SystemBindingChanged(system); + if (args.System is ConstructionSystem system) + SystemBindingChanged(system); } private void OnSystemUnloaded(object? sender, SystemChangedArgs args) { - if (args.System is ConstructionSystem) SystemBindingChanged(null); + if (args.System is ConstructionSystem) + SystemBindingChanged(null); } private void OnViewFavoriteRecipe() { - if (_selected is not ConstructionPrototype recipe) + if (_selected is null) return; if (!_favoritedRecipes.Remove(_selected)) _favoritedRecipes.Add(_selected); - if (_selectedCategory == _favoriteCatName) + if (_selectedCategory == FavoriteCatName) { - if (_favoritedRecipes.Count > 0) - OnViewPopulateRecipes(_constructionView, (string.Empty, _favoriteCatName)); - else - OnViewPopulateRecipes(_constructionView, (string.Empty, string.Empty)); + OnViewPopulateRecipes(_constructionView, + _favoritedRecipes.Count > 0 ? (string.Empty, FavoriteCatName) : (string.Empty, string.Empty)); } PopulateInfo(_selected); @@ -492,6 +522,9 @@ namespace Content.Client.Construction.UI private void BindToSystem(ConstructionSystem system) { _constructionSystem = system; + + OnViewPopulateRecipes(_constructionView, (string.Empty, string.Empty)); + system.ToggleCraftingWindow += SystemOnToggleMenu; system.FlipConstructionPrototype += SystemFlipConstructionPrototype; system.CraftingAvailabilityChanged += SystemCraftingAvailabilityChanged; @@ -533,7 +566,8 @@ namespace Content.Client.Construction.UI if (IsAtFront) { WindowOpen = false; - _uiManager.GetActiveUIWidget().CraftingButton.SetClickPressed(false); // This does not call CraftingButtonToggled + _uiManager.GetActiveUIWidget() + .CraftingButton.SetClickPressed(false); // This does not call CraftingButtonToggled } else _constructionView.MoveToFront(); @@ -541,7 +575,8 @@ namespace Content.Client.Construction.UI else { WindowOpen = true; - _uiManager.GetActiveUIWidget().CraftingButton.SetClickPressed(true); // This does not call CraftingButtonToggled + _uiManager.GetActiveUIWidget() + .CraftingButton.SetClickPressed(true); // This does not call CraftingButtonToggled } } diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index 4c6477f5d20..fd0cedac73e 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -223,6 +223,7 @@ public sealed class CargoTest - type: stack id: StackProto + name: stack-steel spawn: A - type: entity diff --git a/Content.Server/Construction/ConstructionSystem.cs b/Content.Server/Construction/ConstructionSystem.cs index 9d881dcef04..343143b9bd4 100644 --- a/Content.Server/Construction/ConstructionSystem.cs +++ b/Content.Server/Construction/ConstructionSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Construction; using Content.Shared.DoAfter; using JetBrains.Annotations; using Robust.Server.Containers; -using Robust.Shared.Prototypes; using Robust.Shared.Random; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; diff --git a/Content.Shared/Construction/ConstructionGraphNode.cs b/Content.Shared/Construction/ConstructionGraphNode.cs index fd70569e9da..9f910772059 100644 --- a/Content.Shared/Construction/ConstructionGraphNode.cs +++ b/Content.Shared/Construction/ConstructionGraphNode.cs @@ -28,17 +28,17 @@ namespace Content.Shared.Construction [DataField("transform")] public IGraphTransform[] TransformLogic = Array.Empty(); - [DataField("entity", customTypeSerializer: typeof(GraphNodeEntitySerializer), serverOnly:true)] + [DataField("entity", customTypeSerializer: typeof(GraphNodeEntitySerializer))] public IGraphNodeEntity Entity { get; private set; } = new NullNodeEntity(); /// - /// Ignore requests to change the entity if the entity's current prototype inherits from specified replacement + /// Ignore requests to change the entity if the entity's current prototype inherits from specified replacement /// /// - /// When this bool is true and a construction node specifies that the current entity should be replaced with a new entity, if the + /// When this bool is true and a construction node specifies that the current entity should be replaced with a new entity, if the /// current entity has an entity prototype which inherits from the replacement entity prototype, entity replacement will not occur. - /// E.g., if an entity with the 'AirlockCommand' prototype was to be replaced with a new entity that had the 'Airlock' prototype, - /// and 'DoNotReplaceInheritingEntities' was true, the entity would not be replaced because 'AirlockCommand' is derived from 'Airlock' + /// E.g., if an entity with the 'AirlockCommand' prototype was to be replaced with a new entity that had the 'Airlock' prototype, + /// and 'DoNotReplaceInheritingEntities' was true, the entity would not be replaced because 'AirlockCommand' is derived from 'Airlock' /// This will largely be used for construction graphs which have removeable upgrades, such as hacking protections for airlocks, /// so that the upgrades can be removed and you can return to the last primary construction step without replacing the entity /// diff --git a/Content.Server/Construction/NodeEntities/BoardNodeEntity.cs b/Content.Shared/Construction/NodeEntities/BoardNodeEntity.cs similarity index 80% rename from Content.Server/Construction/NodeEntities/BoardNodeEntity.cs rename to Content.Shared/Construction/NodeEntities/BoardNodeEntity.cs index 4f95945262d..c1540c4a646 100644 --- a/Content.Server/Construction/NodeEntities/BoardNodeEntity.cs +++ b/Content.Shared/Construction/NodeEntities/BoardNodeEntity.cs @@ -1,10 +1,8 @@ -using Content.Server.Construction.Components; -using Content.Shared.Construction; using Content.Shared.Construction.Components; using JetBrains.Annotations; -using Robust.Server.Containers; +using Robust.Shared.Containers; -namespace Content.Server.Construction.NodeEntities; +namespace Content.Shared.Construction.NodeEntities; /// /// Works for both and @@ -21,7 +19,7 @@ public sealed partial class BoardNodeEntity : IGraphNodeEntity if (uid == null) return null; - var containerSystem = args.EntityManager.EntitySysManager.GetEntitySystem(); + var containerSystem = args.EntityManager.EntitySysManager.GetEntitySystem(); if (!containerSystem.TryGetContainer(uid.Value, Container, out var container) || container.ContainedEntities.Count == 0) @@ -33,7 +31,7 @@ public sealed partial class BoardNodeEntity : IGraphNodeEntity if (args.EntityManager.TryGetComponent(board, out MachineBoardComponent? machine)) return machine.Prototype; - if(args.EntityManager.TryGetComponent(board, out ComputerBoardComponent? computer)) + if (args.EntityManager.TryGetComponent(board, out ComputerBoardComponent? computer)) return computer.Prototype; return null; diff --git a/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs b/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs index ec5d2cc9da5..0e1a50d9a2a 100644 --- a/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs +++ b/Content.Shared/Construction/Prototypes/ConstructionPrototype.cs @@ -1,8 +1,6 @@ using Content.Shared.Construction.Conditions; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Utility; namespace Content.Shared.Construction.Prototypes; @@ -14,65 +12,57 @@ public sealed partial class ConstructionPrototype : IPrototype /// /// Hide from the construction list /// - [DataField("hide")] + [DataField] public bool Hide = false; /// /// Friendly name displayed in the construction GUI. /// [DataField("name")] - public string Name = string.Empty; + public LocId? SetName; + + public string? Name; /// /// "Useful" description displayed in the construction GUI. /// [DataField("description")] - public string Description = string.Empty; + public LocId? SetDescription; + + public string? Description; /// /// The this construction will be using. /// - [DataField("graph", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] - public string Graph = string.Empty; + [DataField(required: true)] + public ProtoId Graph { get; private set; } = string.Empty; /// /// The target this construction will guide the user to. /// - [DataField("targetNode")] - public string TargetNode = string.Empty; + [DataField(required: true)] + public string TargetNode { get; private set; } = default!; /// /// The starting this construction will start at. /// - [DataField("startNode")] - public string StartNode = string.Empty; - - /// - /// Texture path inside the construction GUI. - /// - [DataField("icon")] - public SpriteSpecifier Icon = SpriteSpecifier.Invalid; - - /// - /// Texture paths used for the construction ghost. - /// - [DataField("layers")] - private List? _layers; + [DataField(required: true)] + public string StartNode { get; private set; } = default!; /// /// If you can start building or complete steps on impassable terrain. /// - [DataField("canBuildInImpassable")] + [DataField] public bool CanBuildInImpassable { get; private set; } /// /// If not null, then this is used to check if the entity trying to construct this is whitelisted. /// If they're not whitelisted, hide the item. /// - [DataField("entityWhitelist")] - public EntityWhitelist? EntityWhitelist = null; + [DataField] + public EntityWhitelist? EntityWhitelist { get; private set; } - [DataField("category")] public string Category { get; private set; } = ""; + [DataField] public string Category { get; private set; } = string.Empty; [DataField("objectType")] public ConstructionType Type { get; private set; } = ConstructionType.Structure; @@ -80,23 +70,22 @@ public sealed partial class ConstructionPrototype : IPrototype [IdDataField] public string ID { get; private set; } = default!; - [DataField("placementMode")] + [DataField] public string PlacementMode = "PlaceFree"; /// /// Whether this construction can be constructed rotated or not. /// - [DataField("canRotate")] + [DataField] public bool CanRotate = true; /// /// Construction to replace this construction with when the current one is 'flipped' /// - [DataField("mirror", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? Mirror; + [DataField] + public ProtoId? Mirror { get; private set; } public IReadOnlyList Conditions => _conditions; - public IReadOnlyList Layers => _layers ?? new List { Icon }; } public enum ConstructionType diff --git a/Content.Shared/Construction/Steps/ArbitraryInsertConstructionGraphStep.cs b/Content.Shared/Construction/Steps/ArbitraryInsertConstructionGraphStep.cs index 49d429aa267..7615460001e 100644 --- a/Content.Shared/Construction/Steps/ArbitraryInsertConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/ArbitraryInsertConstructionGraphStep.cs @@ -5,24 +5,26 @@ namespace Content.Shared.Construction.Steps { public abstract partial class ArbitraryInsertConstructionGraphStep : EntityInsertConstructionGraphStep { - [DataField("name")] public string Name { get; private set; } = string.Empty; + [DataField] public LocId Name { get; private set; } = string.Empty; - [DataField("icon")] public SpriteSpecifier? Icon { get; private set; } + [DataField] public SpriteSpecifier? Icon { get; private set; } public override void DoExamine(ExaminedEvent examinedEvent) { if (string.IsNullOrEmpty(Name)) return; - examinedEvent.PushMarkup(Loc.GetString("construction-insert-arbitrary-entity", ("stepName", Name))); + var stepName = Loc.GetString(Name); + examinedEvent.PushMarkup(Loc.GetString("construction-insert-arbitrary-entity", ("stepName", stepName))); } public override ConstructionGuideEntry GenerateGuideEntry() { + var stepName = Loc.GetString(Name); return new ConstructionGuideEntry { Localization = "construction-presenter-arbitrary-step", - Arguments = new (string, object)[]{("name", Name)}, + Arguments = new (string, object)[]{("name", stepName)}, Icon = Icon, }; } diff --git a/Content.Shared/Construction/Steps/ComponentConstructionGraphStep.cs b/Content.Shared/Construction/Steps/ComponentConstructionGraphStep.cs index 5c28d3b05f3..95f0eefb8a7 100644 --- a/Content.Shared/Construction/Steps/ComponentConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/ComponentConstructionGraphStep.cs @@ -26,7 +26,7 @@ namespace Content.Shared.Construction.Steps ("componentName", Component))// Terrible. : Loc.GetString( "construction-insert-exact-entity", - ("entityName", Name))); + ("entityName", Loc.GetString(Name)))); } } } diff --git a/Content.Shared/Construction/Steps/MaterialConstructionGraphStep.cs b/Content.Shared/Construction/Steps/MaterialConstructionGraphStep.cs index eec8d89b808..b1ae9cbdf7a 100644 --- a/Content.Shared/Construction/Steps/MaterialConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/MaterialConstructionGraphStep.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Examine; using Content.Shared.Stacks; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Construction.Steps { @@ -11,16 +10,17 @@ namespace Content.Shared.Construction.Steps { // TODO: Make this use the material system. // TODO TODO: Make the material system not shit. - [DataField("material", required:true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string MaterialPrototypeId { get; private set; } = "Steel"; + [DataField("material", required:true)] + public ProtoId MaterialPrototypeId { get; private set; } - [DataField("amount")] public int Amount { get; private set; } = 1; + [DataField] public int Amount { get; private set; } = 1; public override void DoExamine(ExaminedEvent examinedEvent) { - var material = IoCManager.Resolve().Index(MaterialPrototypeId); + var material = IoCManager.Resolve().Index(MaterialPrototypeId); + var materialName = Loc.GetString(material.Name, ("amount", Amount)); - examinedEvent.PushMarkup(Loc.GetString("construction-insert-material-entity", ("amount", Amount), ("materialName", material.Name))); + examinedEvent.PushMarkup(Loc.GetString("construction-insert-material-entity", ("amount", Amount), ("materialName", materialName))); } public override bool EntityValid(EntityUid uid, IEntityManager entityManager, IComponentFactory compFactory) @@ -40,12 +40,13 @@ namespace Content.Shared.Construction.Steps public override ConstructionGuideEntry GenerateGuideEntry() { - var material = IoCManager.Resolve().Index(MaterialPrototypeId); + var material = IoCManager.Resolve().Index(MaterialPrototypeId); + var materialName = Loc.GetString(material.Name, ("amount", Amount)); return new ConstructionGuideEntry() { Localization = "construction-presenter-material-step", - Arguments = new (string, object)[]{("amount", Amount), ("material", material.Name)}, + Arguments = new (string, object)[]{("amount", Amount), ("material", materialName)}, Icon = material.Icon, }; } diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 72ea9dba655..228926fac45 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -1,7 +1,4 @@ -using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Text; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Ghost; using Content.Shared.Interaction; diff --git a/Content.Shared/Stacks/StackPrototype.cs b/Content.Shared/Stacks/StackPrototype.cs index f108419a9ef..d72380da62e 100644 --- a/Content.Shared/Stacks/StackPrototype.cs +++ b/Content.Shared/Stacks/StackPrototype.cs @@ -1,5 +1,4 @@ using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; namespace Content.Shared.Stacks; @@ -16,7 +15,7 @@ public sealed partial class StackPrototype : IPrototype /// /// This is a localization string ID. [DataField] - public string Name { get; private set; } = string.Empty; + public LocId Name { get; private set; } = string.Empty; /// /// An icon that will be used to represent this stack type. diff --git a/Resources/Locale/en-US/construction/ui/construction-menu.ftl b/Resources/Locale/en-US/construction/ui/construction-menu.ftl index 4d100247856..4812604d608 100644 --- a/Resources/Locale/en-US/construction/ui/construction-menu.ftl +++ b/Resources/Locale/en-US/construction/ui/construction-menu.ftl @@ -5,4 +5,5 @@ construction-menu-place-ghost = Place construction ghost construction-menu-clear-all = Clear All construction-menu-eraser-mode = Eraser Mode construction-menu-craft = Craft +construction-menu-search = Search construction-menu-grid-view = Grid View diff --git a/Resources/Locale/en-US/recipes/components.ftl b/Resources/Locale/en-US/recipes/components.ftl new file mode 100644 index 00000000000..236097532cd --- /dev/null +++ b/Resources/Locale/en-US/recipes/components.ftl @@ -0,0 +1,7 @@ +construction-graph-component-any-computer-circuit-board = any computer circuit board +construction-graph-component-door-electronics-circuit-board = door electronics circuit board +construction-graph-component-flash = flash +construction-graph-component-second-flash = second flash +construction-graph-component-power-cell = power cell +construction-graph-component-apc-electronics = APC electronics +construction-graph-component-payload-trigger = trigger diff --git a/Resources/Locale/en-US/recipes/recipes.ftl b/Resources/Locale/en-US/recipes/recipes.ftl new file mode 100644 index 00000000000..d09939692d9 --- /dev/null +++ b/Resources/Locale/en-US/recipes/recipes.ftl @@ -0,0 +1,2 @@ +recipes-secret-door-name = secret door +recipes-secret-door-desc = A secret door disguised as a wall. The perfect solution for hiding your shady dealings. diff --git a/Resources/Locale/en-US/recipes/tags.ftl b/Resources/Locale/en-US/recipes/tags.ftl new file mode 100644 index 00000000000..baae9a13034 --- /dev/null +++ b/Resources/Locale/en-US/recipes/tags.ftl @@ -0,0 +1,144 @@ +# clown +construction-graph-tag-banana-peel = a banana peel +construction-graph-tag-clown-suit = a clown suit +construction-graph-tag-clown-shoes = clown shoes +construction-graph-tag-clown-mask = a clown mask +construction-graph-tag-clown-recorder = clown recorder +construction-graph-tag-clown-bike-horn = bike horn +construction-graph-tag-clowne-horn = broken bike horn +construction-graph-tag-happy-honk-meal = happy honk meal +construction-graph-tag-woeful-cluwne-meal = woeful cluwne meal + +# mime +construction-graph-tag-suspenders = suspenders +construction-graph-tag-mime-meal = mime edition happy honk meal + +# crayon +construction-graph-tag-purple-crayon = purple crayon +construction-graph-tag-red-crayon = red crayon +construction-graph-tag-yellow-crayon = yellow crayon +construction-graph-tag-black-crayon = black crayon + +# eva +construction-graph-tag-eva-suit = an EVA suit +construction-graph-tag-eva-helmet = an EVA helmet + +# hud +construction-graph-tag-security-hud = security hud +construction-graph-tag-medical-hud = medical hud + +# security +construction-graph-tag-sun-glasses = sun glasses +construction-graph-tag-security-helmet = security helmet + +# materials +construction-graph-tag-capacitor = capacitor +construction-graph-tag-voice-trigger = a voice trigger +construction-graph-tag-signal-trigger = a signal trigger +construction-graph-tag-proximity-sensor = proximity sensor +construction-graph-tag-glass-shard = a glass shard +construction-graph-tag-plasma-glass-shard = a plasma glass shard +construction-graph-tag-uranium-glass-shard = a uranium glass shard +construction-graph-tag-reinforced-glass-shard = a reinforced glass shard +construction-graph-tag-grey-flatcap = a grey flatcap +construction-graph-tag-brown-flatcap = a brown flatcap +construction-graph-tag-cuffs = cuffs +construction-graph-tag-payload = payload +construction-graph-tag-empty-can = an empty can +construction-graph-tag-igniter = an igniter +construction-graph-tag-modular-receiver = modular receiver +construction-graph-tag-power-cell-small = power cell small +construction-graph-tag-power-cell = power cell +construction-graph-tag-potato-battery = a potato battery +construction-graph-tag-super-compact-ai-chip = a super-compact AI chip + +# other +construction-graph-tag-light-bulb = light bulb +construction-graph-tag-radio = radio +construction-graph-tag-pipe = pipe +construction-graph-tag-human-head = human head +construction-graph-tag-bucket = bucket +construction-graph-tag-borg-arm = borg arm +construction-graph-tag-borg-head = borg head +construction-graph-tag-medkit = medkit +construction-graph-tag-flower = flower +construction-graph-tag-ambrosia = ambrosia +construction-graph-tag-rifle-stock = rifle stock +construction-graph-tag-match-stick = match stick +construction-graph-tag-potato = a potato +construction-graph-tag-wheat-bushel = wheat bushel +construction-graph-tag-corgi-hide = corgi hide + +# toys +construction-graph-tag-rubber-ducky = a rubber ducky +construction-graph-tag-ghost = ghost soft toy +construction-graph-tag-ectoplasm = ectoplasm +construction-graph-tag-lizard-plushie = lizard plushie + +# carpet +construction-graph-tag-black-carpet = black carpet +construction-graph-tag-blue-carpet = blue carpet +construction-graph-tag-cyan-carpet = cyan carpet +construction-graph-tag-green-carpet = green carpet +construction-graph-tag-orange-carpet = orange carpet +construction-graph-tag-pink-carpet = pink carpet +construction-graph-tag-purple-carpet = purple carpet +construction-graph-tag-red-carpet = red carpet +construction-graph-tag-white-carpet = white carpet + +# mechs +construction-graph-tag-hamtr-central-control-module = HAMTR central control module +construction-graph-tag-hamtr-peripherals-control-module = HAMTR peripherals control module +construction-graph-tag-honk-central-control-module = H.O.N.K. central control module +construction-graph-tag-honk-peripherals-control-module = H.O.N.K. peripherals control module +construction-graph-tag-honk-weapon-control-and-targeting-module = H.O.N.K. weapon control and targeting module +construction-graph-tag-ripley-central-control-module = ripley central control module +construction-graph-tag-ripley-peripherals-control-module = ripley peripherals control module + +# structures +construction-graph-tag-door-electronics-circuit-board = door electronics circuit board +construction-graph-tag-firelock-electronics-circuit-board = firelock electronics circuit board +construction-graph-tag-conveyor-belt-assembly = conveyor belt assembly + +# tools +construction-graph-tag-multitool = a multitool +construction-graph-tag-health-analyzer = health analyzer + +# utils +construction-graph-tag-air-alarm-electronics = air alarm electronics +construction-graph-tag-fire-alarm-electronics = fire alarm electronics +construction-graph-tag-mailing-unit-electronics = mailing unit electronics +construction-graph-tag-intercom-electronics = intercom electronics +construction-graph-tag-solar-assembly-parts = solar assembly parts +construction-graph-tag-solar-tracker-electronics = solar tracker electronics +construction-graph-tag-station-map-electronics = station map electronics +construction-graph-tag-signal-timer-electronics = signal timer electronics +construction-graph-tag-screen-timer-electronics = screen timer electronics +construction-graph-tag-brig-timer-electronics = brig timer electronics +construction-graph-tag-wallmount-generator-circuit-board = wallmount generator circuit board +construction-graph-tag-wallmount-apu-circuit-board = wallmount APU circuit board +construction-graph-tag-wallmount-substation-circuit-board = wallmount substation circuit board +construction-graph-tag-surveillance-camera-monitor-board = surveillance camera monitor board +construction-graph-tag-television-board = television board +construction-graph-tag-freezer-electronics = freezer electronics + +# crystals +construction-graph-tag-cyan-crystal-shard = cyan crystal shard +construction-graph-tag-blue-crystal-shard = blue crystal shard +construction-graph-tag-pink-crystal-shard = pink crystal shard +construction-graph-tag-orange-crystal-shard = orange crystal shard +construction-graph-tag-red-crystal-shard = red crystal shard +construction-graph-tag-green-crystal-shard = green crystal shard +construction-graph-tag-yellow-crystal-shard = yellow crystal shard +construction-graph-tag-black-crystal-shard = black crystal shard + +# unknown +construction-graph-tag-weapon-pistol-chimp-upgrade-kit = pistol CHIMP upgrade kit +construction-graph-tag-torch = torch + +# atmos +construction-graph-tag-fire-extinguisher = fire extinguisher +construction-graph-tag-fire-helmet = fire helmet + +# salvage +construction-graph-tag-spationaut-hardsuit = spationaut hardsuit diff --git a/Resources/Locale/en-US/stack/stacks.ftl b/Resources/Locale/en-US/stack/stacks.ftl new file mode 100644 index 00000000000..f2853031921 --- /dev/null +++ b/Resources/Locale/en-US/stack/stacks.ftl @@ -0,0 +1,234 @@ +stack-steel = steel +stack-bananium = bananium +stack-glass = glass +stack-plasteel = plasteel +stack-brass = brass +stack-plastic = plastic +stack-silver = silver +stack-gold = gold +stack-reinforced-glass = reinforced glass +stack-plasma-glass = plasma glass +stack-uranium = uranium +stack-uranium-glass = uranium glass +stack-clockwork-glass = clockwork glass +stack-reinforced-plasma-glass = reinforced plasma glass +stack-reinforced-uranium-glass = reinforced uranium glass +stack-gunpowder = gunpowder +stack-cardboard = cardboard + +stack-bones = {$amount -> + [1] bone + *[other] bones +} +stack-cloth = {$amount -> + [1] cloth + *[other] cloths +} +stack-lv-cable = {$amount -> + [1] lv cable + *[other] lv cables +} +stack-mv-cable = {$amount -> + [1] mv cable + *[other] mv cables +} +stack-hv-cable = {$amount -> + [1] hv cable + *[other] hv cables +} +stack-wood-plank = {$amount -> + [1] wood plank + *[other] wood planks +} +stack-durathread = {$amount -> + [1] durathread + *[other] durathreads +} +stack-rods = {$amount -> + [1] rod + *[other] rods +} +stack-meat-sheet = {$amount -> + [1] meat sheet + *[other] meat sheets +} +stack-space-carp-tooth = space carp {$amount -> + [1] tooth + *[other] teeth +} +stack-paper = {$amount -> + [1] paper + *[other] papers +} +stack-diamond = {$amount -> + [1] diamond + *[other] diamonds +} +stack-silk = {$amount -> + [1] silk + *[other] silks +} +stack-cotton = {$amount -> + [1] cotton + *[other] cottons +} +stack-artifact-fragment = artifact {$amount -> + [1] fragment + *[other] fragments +} + +# best materials +stack-ground-tobacco = ground tobacco +stack-ground-cannabis = ground cannabis +stack-ground-rainbow-cannabis = ground rainbow cannabis +stack-dried-tobacco-leaves = dried tobacco leaves +stack-dried-cannabis-leaves = dried cannabis leaves +stack-dried-rainbow-cannabis-leaves = dried rainbow cannabis leaves + +stack-cigarette-filter = cigarette {$amount -> + [1] filter + *[other] filters +} +stack-rolling-paper = rolling {$amount -> + [1] paper + *[other] papers +} + +stack-fulton = fulton +stack-credit = speso +stack-plasma = plasma +stack-biomass = biomass +stack-pyrotton = pyrotton +stack-sharkminnow-tooth = sharkminnow tooth +stack-goliath-hide = goliath hide +stack-telecrystal = telecrystal +stack-gold-ore = gold ore +stack-rough-diamond = rough diamond +stack-iron-ore = iron ore +stack-plasma-ore = plasma ore +stack-silver-ore = silver ore +stack-space-quartz = space quartz +stack-uranium-ore = uranium ore +stack-bananium-ore = bananium ore +stack-coal = coal +stack-salt = salt +stack-inflatable-wall = inflatable wall +stack-inflatable-door = inflatable door +stack-ointment = ointment +stack-aloe-cream = aloe cream +stack-gauze = gauze +stack-brutepack = brutepack +stack-bloodpack = bloodpack +stack-medicated-suture = medicated-suture +stack-regenerative-mesh = regenerative-mesh +stack-capacitor = capacitor +stack-micro-manipulator = micro manipulator +stack-matter-bin = matter bin +stack-pancake = pancake +stack-blueberry-pancake = blueberry pancake +stack-chocolate-chip-pancake = chocolate chip pancake +stack-pizza-box = pizza box +stack-dark-tile = dark tile +stack-dark-steel-diagonal-mini-tile = dark steel diagonal mini tile +stack-dark-steel-diagonal-tile = dark steel diagonal tile +stack-dark-steel-herringbone = dark steel herringbone +stack-dark-steel-mini-tile = dark steel mini tile +stack-dark-steel-mono-tile = dark steel mono tile +stack-dark-steel-pavement = dark steel pavement +stack-dark-steel-vertical-pavement = dark steel vertical pavement +stack-offset-dark-steel-tile = offset dark steel tile +stack-offset-steel-tile = offset steel tile +stack-steel-diagonal-mini-tile = steel diagonal mini tile +stack-steel-diagonal-tile = steel diagonal tile +stack-steel-herringbone = steel herringbone +stack-steel-mini-tile = steel mini tile +stack-steel-mono-tile = steel mono tile +stack-steel-pavement = steel pavement +stack-steel-vertical-pavement = steel vertical pavement +stack-white-tile = white tile +stack-offset-white-steel-tile = offset white steel tile +stack-white-steel-diagonal-mini-tile = white steel diagonal mini tile +stack-white-steel-diagonal-tile = white steel diagonal tile +stack-white-steel-herringbone = white steel herringbone +stack-white-steel-mini-tile = white steel mini tile +stack-white-steel-mono-tile = white steel mono tile +stack-white-steel-pavement = white steel pavement +stack-white-steel-vertical-pavement = white steel vertical pavement +stack-steel-dark-checker-tile = steel dark checker tile +stack-steel-light-checker-tile = steel light checker tile +stack-steel-tile = steel tile +stack-wood-floor = wood floor +stack-techmaint-floor = techmaint floor +stack-freezer-tile = freezer tile +stack-showroom-tile = showroom tile +stack-green-circuit-floor = green-circuit floor +stack-gold-floor = gold floor +stack-mono-tile = mono tile +stack-filled-brass-plate = filled brass plate +stack-smooth-brass-plate = smooth brass plate +stack-linoleum-floor = linoleum floor +stack-hydro-tile = hydro tile +stack-lime-tile = lime tile +stack-dirty-tile = dirty tile +stack-white-shuttle-tile = white shuttle tile +stack-blue-shuttle-tile = blue shuttle tile +stack-orange-shuttle-tile = orange shuttle tile +stack-purple-shuttle-tile = purple shuttle tile +stack-red-shuttle-tile = red shuttle tile +stack-grey-shuttle-tile = grey shuttle tile +stack-black-shuttle-tile = black shuttle tile +stack-eighties-floor-tile = eighties floor tile +stack-blue-arcade-tile = blue arcade tile +stack-red-arcade-tile = red arcade tile +stack-red-carpet-tile = red carpet tile +stack-block-carpet-tile = block carpet tile +stack-blue-carpet-tile = blue carpet tile +stack-green-carpet-tile = green carpet tile +stack-orange-carpet-tile = orange carpet tile +stack-skyblue-carpet-tile = skyblue carpet tile +stack-purple-carpet-tile = purple carpet tile +stack-pink-carpet-tile = pink carpet tile +stack-cyan-carpet-tile = cyan carpet tile +stack-white-carpet-tile = white carpet tile +stack-clown-carpet-tile = clown carpet tile +stack-office-carpet-tile = office carpet tile +stack-boxing-ring-tile = boxing ring tile +stack-gym-floor-tile = gym floor tile +stack-elevator-shaft-tile = elevator shaft tile +stack-rock-vault-tile = rock vault tile +stack-blue-floor-tile = blue floor tile +stack-mining-floor-tile = mining floor tile +stack-dark-mining-floor-tile = dark mining floor tile +stack-light-mining-floor-tile = light mining floor tile +stack-item-bar-floor-tile = item bar floor tile +stack-clown-floor-tile = clown floor tile +stack-mime-floor-tile = mime floor tile +stack-kitchen-floor-tile = kitchen floor tile +stack-laundry-floor-tile = laundry floor tile +stack-concrete-tile = concrete tile +stack-concrete-mono-tile = concrete mono tile +stack-concrete-smooth = concrete smooth +stack-gray-concrete-tile = gray concrete tile +stack-gray-concrete-mono-tile = gray concrete mono tile +stack-gray-concrete-smooth = gray concrete smooth +stack-old-concrete-tile = old concrete tile +stack-old-concrete-mono-tile = old concrete mono tile +stack-old-concrete-smooth = old concrete smooth +stack-silver-floor-tile = silver floor tile +stack-bcircuit-floor-tile = bcircuit floor tile +stack-grass-floor-tile = grass floor tile +stack-grass-jungle-floor-tile = grass jungle floor tile +stack-snow-floor-tile = snow floor tile +stack-wood-patter-floor = wood pattern floor +stack-flesh-floor = flesh floor +stack-steel-maint-floor = steel maint floor +stack-grating-maint-floor = grating maint floor +stack-web-tile = web tile +stack-astro-grass-floor = astro-grass floor +stack-mowed-astro-grass-floor = mowed astro-grass floor +stack-jungle-astro-grass-floor = jungle astro-grass floor +stack-astro-ice-floor = astro-ice floor +stack-astro-snow-floor = astro-snow floor +stack-large-wood-floor = large wood floor +stack-red-circuit-floor = red-circuit floor +stack-asteroid-astro-sand-floor = asteroid astro-sand floor diff --git a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml index 71cb91e7591..81292618cd5 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml @@ -35,7 +35,7 @@ - type: StackLayerThreshold thresholds: [10, 100, 500, 1000, 5000, 10000, 25000, 50000, 100000, 1000000] - type: Sprite - sprite: _NF/Objects/Economy/cash.rsi # Frontier: larger denominations + sprite: Objects/Economy/cash.rsi state: cash layers: - state: cash @@ -52,20 +52,20 @@ mask: - ItemMask - type: Appearance - - type: CargoSellBlacklist + - type: CargoSellBlacklist # Frontier - type: material id: Credit name: speso unit: materials-unit-bill stackEntity: SpaceCash - icon: { sprite: _NF/Objects/Economy/cash.rsi, state: cash } # Frontier: use Frontier sprite set + icon: { sprite: /Textures/Objects/Economy/cash.rsi, state: cash } price: 1 - type: stack id: Credit - name: speso - icon: { sprite: _NF/Objects/Economy/cash.rsi, state: cash } # Frontier: use Frontier sprite set + name: stack-credit + icon: { sprite: /Textures/Objects/Economy/cash.rsi, state: cash } spawn: SpaceCash - type: entity @@ -74,7 +74,7 @@ suffix: 10 components: - type: Icon - sprite: _NF/Objects/Economy/cash.rsi # Frontier: use Frontier sprite set + sprite: Objects/Economy/cash.rsi state: cash_10 - type: Stack count: 10 @@ -85,7 +85,7 @@ suffix: 100 components: - type: Icon - sprite: _NF/Objects/Economy/cash.rsi # Frontier: use Frontier sprite set + sprite: Objects/Economy/cash.rsi state: cash_100 - type: Stack count: 100 @@ -96,7 +96,7 @@ suffix: 500 components: - type: Icon - sprite: _NF/Objects/Economy/cash.rsi # Frontier: use Frontier sprite set + sprite: Objects/Economy/cash.rsi state: cash_500 - type: Stack count: 500 @@ -107,7 +107,7 @@ suffix: 1000 components: - type: Icon - sprite: _NF/Objects/Economy/cash.rsi # Frontier: use Frontier sprite set + sprite: Objects/Economy/cash.rsi state: cash_1000 - type: Stack count: 1000 @@ -118,7 +118,7 @@ suffix: 2500 components: - type: Icon - sprite: _NF/Objects/Economy/cash.rsi # Frontier: use Frontier sprite set + sprite: Objects/Economy/cash.rsi state: cash_1000 - type: Stack count: 2500 @@ -173,7 +173,7 @@ suffix: 1000000 components: - type: Icon - sprite: _NF/Objects/Economy/cash.rsi # Frontier: use Frontier sprite set - state: cash_250000 # Frontier: cash_1000000