using System.Numerics; using Content.Shared.Containers.ItemSlots; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Containers; using Robust.Shared.Enums; using Robust.Shared.Graphics; using Robust.Shared.Reflection; using Robust.Shared.Timing; namespace Content.Client._White.ItemSlotRenderer; /// /// I can feel my grip on reality slowly slipping. /// public sealed class ItemSlotRendererSystem : EntitySystem { [Dependency] private readonly IReflectionManager _reflection = default!; [Dependency] private readonly ItemSlotsSystem _slot = default!; [Dependency] private readonly IClyde _clyde = default!; [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnInsertIntoContainer); SubscribeLocalEvent(OnRemoveFromContainer); } private void OnInsertIntoContainer(EntityUid uid, ItemSlotRendererComponent comp, EntInsertedIntoContainerMessage args) { if (args.Container is not ContainerSlot || !_timing.IsFirstTimePredicted) return; comp.CachedEntities[args.Container.ID] = args.Entity; } private void OnRemoveFromContainer(EntityUid uid, ItemSlotRendererComponent comp, EntRemovedFromContainerMessage args) { if (args.Container is not ContainerSlot || !_timing.IsFirstTimePredicted) return; comp.CachedEntities[args.Container.ID] = null; } private void OnRemove(EntityUid uid, ItemSlotRendererComponent comp, ComponentRemove args) { foreach (var (_, renderTexture) in comp.CachedRT) renderTexture.Dispose(); } private void OnStartup(EntityUid uid, ItemSlotRendererComponent comp, ComponentStartup args) { if (!TryComp(uid, out var sprite)) { Log.Error($"ItemSlotRendererComponent requires SpriteComponent to work, but {ToPrettyString(uid)} did not have one. Removing ItemSlotRenderer."); RemComp(uid); return; } foreach (var kvp in comp.PrototypeLayerMappings) { (var slotId, object mapKey) = kvp; var isEnum = false; if (_reflection.TryParseEnumReference((string) mapKey, out var @enum)) { mapKey = @enum; isEnum = true; } if (!sprite.LayerMapTryGet(mapKey, out _) && comp.ErrorOnMissing) { Log.Warning($"ItemSlotRenderer: Tried to add a missing layer under the {(isEnum ? "enum" : "string")} key {mapKey}. Skipping missing layer. If this is unwanted, set component's AddMissingLayers to true."); continue; } if(_slot.TryGetSlot(uid, slotId, out var slot)) comp.CachedEntities[slotId] = slot.Item; comp.LayerMappings.Add((mapKey, slotId)); comp.CachedRT.Add( slotId, _clyde.CreateRenderTarget(comp.RenderTargetSize, new (RenderTargetColorFormat.Rgba8Srgb), new TextureSampleParameters { Filter = false, }, $"{slotId}-itemrender-rendertarget")); } } } /// /// Doesn't actually render anything by itself. I'd place this code in a system's FrameUpdate, /// but I need to somehow acquire a draw handle to draw an entity to a texture. /// public sealed class SpriteToLayerBullshitOverlay : Overlay { [Dependency] private readonly EntityManager _entMan = default!; public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld; public SpriteToLayerBullshitOverlay() { IoCManager.InjectDependencies(this); } protected override void Draw(in OverlayDrawArgs args) { var handle = args.ScreenHandle; var query = _entMan.EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp, out var sprite)) { for (var i = 0; i < comp.LayerMappings.Count; i++) { var (layerKey, slotId) = comp.LayerMappings[i]; if (!sprite.LayerMapTryGet(layerKey, out var layerIndex) || !sprite.TryGetLayer(layerIndex, out var layer)) // verify that the layer actually exists continue; // if for some reason we can't render the item to a texture (or there is no item to render), // assign an "empty" texture to the layer if (!comp.CachedEntities.TryGetValue(slotId, out var item) || !item.HasValue || !comp.CachedRT.TryGetValue(slotId, out var renderTarget)) { if (layer.Texture != Texture.Transparent) sprite.LayerSetTexture(layerIndex, Texture.Transparent); continue; } handle.RenderInRenderTarget( renderTarget, () => { handle.DrawEntity(item.Value, renderTarget.Size / 2, Vector2.One, 0); // If this throws due to a missing spritecomp, it's your fault. }, Color.Transparent); sprite.LayerSetTexture(layerIndex, renderTarget.Texture); } } } }