// SPDX-FileCopyrightText: 2025 Ark // SPDX-FileCopyrightText: 2025 Redrover1760 // SPDX-FileCopyrightText: 2025 ark1368 // // SPDX-License-Identifier: AGPL-3.0-or-later using System.Numerics; using System.Runtime.InteropServices; using Content.Client.Resources; using Content.Shared._Crescent.ShipShields; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Shared.Enums; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; namespace Content.Client._Crescent.ShipShields; public sealed class ShipShieldOverlay : Overlay { private readonly FixtureSystem _fixture; private readonly SharedPhysicsSystem _physics; private readonly IResourceCache _resourceCache; private readonly IEntityManager _entManager; private readonly ShaderInstance _unshadedShader; private readonly List _verts = new(128); // Mono public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld; public ShipShieldOverlay(IEntityManager entityManager, IPrototypeManager prototypeManager, IResourceCache resourceCache) { _resourceCache = resourceCache; _entManager = entityManager; _fixture = _entManager.EntitySysManager.GetEntitySystem(); _physics = _entManager.EntitySysManager.GetEntitySystem(); _unshadedShader = prototypeManager.Index("unshaded").Instance(); ZIndex = 8; } protected override void Draw(in OverlayDrawArgs args) { var handle = args.WorldHandle; handle.UseShader(_unshadedShader); var enumerator = _entManager.AllEntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var visuals, out var fixtures, out var xform)) { // VANTABLACK OVERDRAW FROM THE DEPTHS OF LAGHELL if (xform.MapID != args.MapId) continue; // TODO: We can probably at least test its parent grid is in PVS range...? var fixture = _fixture.GetFixtureOrNull(uid, "shield", fixtures); if (fixture is not { Shape: ChainShape chain }) continue; var texture = _resourceCache.GetTexture("/Textures/_Crescent/ShipShields/shieldtex.png"); DrawShield(handle, uid, chain, xform, texture, visuals.ShieldColor, _verts); _verts.Clear(); // Clear for next shield - Mono } } private void DrawShield( DrawingHandleWorld handle, EntityUid uid, ChainShape chain, TransformComponent xform, Texture tex, Color color, List verts) { // The vertices of this fixture are defined relative to local position, // so we'll have to add them to this and then use the matrix to put them back in world position. var localPos = xform.LocalPosition; // If "Transforms" ever get deprecated go ahead and check how DebugPHysicsSystem is drawing chains in this hellworld future var transform = _physics.GetPhysicsTransform(uid); for (int i = 1; i < chain.Count; i++) { // top left corner var leftVertex = VertexToWorldPos(chain.Vertices[i - 1], transform); // top right corner var rightVertex = VertexToWorldPos(chain.Vertices[i], transform); // bottom left corner var leftCorner = Corner(localPos, leftVertex, transform); // bottom right corner var rightCorner = Corner(localPos, rightVertex, transform); // Assemble 2 triangles. // Triangle one: top left, top right, bottom left verts.Add(new DrawVertexUV2D(leftVertex, new Vector2(0, 1))); verts.Add(new DrawVertexUV2D(rightVertex, new Vector2(1, 1))); verts.Add(new DrawVertexUV2D(leftCorner, Vector2.Zero)); // Triangle two: top right, bottom left, bottom right verts.Add(new DrawVertexUV2D(rightVertex, new Vector2(1, 1))); verts.Add(new DrawVertexUV2D(leftCorner, Vector2.Zero)); verts.Add(new DrawVertexUV2D(rightCorner, new Vector2(1, 0))); } handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, texture: tex, CollectionsMarshal.AsSpan(verts), color); } private static Vector2 VertexToWorldPos(Vector2 vertexPos, Transform transform) { return Transform.Mul(transform, vertexPos); } private static Vector2 Corner(Vector2 localPos, Vector2 vertexPos, Transform transform, float radius = 1.3f) { var localXform = Transform.Mul(transform, localPos); var cornerPos = Vector2.Subtract(vertexPos, localXform); cornerPos.Normalize(); cornerPos *= radius; return Vector2.Subtract(vertexPos, cornerPos); } }