Localizable craftmenu (#32339)
* Now the name of the target craft items is taken directly from the prototypes * Deleting unnecessary fields * Deleting unnecessary fields * Added suffix field * Added override via localization keys * My favorite ItemList and TextureRect have been replaced with ListContainer and EntityPrototypeView * Fix suffix * Fix construction ghosts... maybe * Remove suffix from UI * Suffixes have been removed from prototypes * Added a description for the secret door * Fix search..? * The Icon field of ConstructionPrototype has been removed * StackPrototypes used in the construction menu have been localized * TagConstructionGraphStep used in the construction menu have been localized * The search bar has been localized * Fix localization and prototypes * Recipes are now only loaded when the crafting window is opened. * Fix crooked merge grid of the crafting menu. * Localization update * Fix cyborg graph * Revert "Recipes are now only loaded when the crafting window is opened." This reverts commit 97749483542c2d6272bda16edf49612c69a0761a. * Fix loc * fix merge * Fix upstream * Some of the logic has been moved to Shared * fix * Small adjustments * Very small change --------- Co-authored-by: EmoGarbage404 <retron404@gmail.com>
This commit is contained in:
@@ -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<int, EntityUid> _ghosts = new();
|
||||
private readonly Dictionary<string, ConstructionGuide> _guideCache = new();
|
||||
|
||||
private readonly Dictionary<string, string> _recipesMetadataCache = [];
|
||||
|
||||
public bool CraftingEnabled { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -40,6 +41,8 @@ namespace Content.Client.Construction
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
WarmupRecipesCache();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
SubscribeLocalEvent<LocalPlayerAttachedEvent>(HandlePlayerAttached);
|
||||
SubscribeNetworkEvent<AckStructureConstructionMessage>(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<ConstructionPrototype>())
|
||||
{
|
||||
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<ConstructionGraphNode>();
|
||||
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<TransformComponent>(ghost.Value).LocalRotation = dir.ToAngle();
|
||||
_ghosts.Add(comp.GhostId, ghost.Value);
|
||||
|
||||
var sprite = EntityManager.GetComponent<SpriteComponent>(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<SpriteComponent>(dummy);
|
||||
EntityManager.System<AppearanceSystem>().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<WallMountComponent>(ghost.Value).Arc = new(Math.Tau);
|
||||
|
||||
Reference in New Issue
Block a user