diff --git a/Content.Client/Pinpointer/NavMapSystem.cs b/Content.Client/Pinpointer/NavMapSystem.cs index d346d90904..bd7dfc1117 100644 --- a/Content.Client/Pinpointer/NavMapSystem.cs +++ b/Content.Client/Pinpointer/NavMapSystem.cs @@ -33,6 +33,9 @@ public sealed class NavMapSystem : SharedNavMapSystem component.Beacons.Clear(); component.Beacons.AddRange(state.Beacons); + + component.Airlocks.Clear(); + component.Airlocks.AddRange(state.Airlocks); } } diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs index a748dc4a6d..d3c505e49f 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -25,7 +25,7 @@ namespace Content.Client.Pinpointer.UI; public partial class NavMapControl : MapGridControl { [Dependency] private readonly IEntityManager _entManager = default!; - private readonly SharedTransformSystem _transformSystem = default!; + private readonly SharedTransformSystem _transformSystem; public EntityUid? Owner; public EntityUid? MapUid; @@ -37,7 +37,7 @@ public partial class NavMapControl : MapGridControl // Tracked data public Dictionary TrackedCoordinates = new(); public Dictionary TrackedEntities = new(); - public Dictionary> TileGrid = default!; + public Dictionary>? TileGrid = default!; // Default colors public Color WallColor = new(102, 217, 102); @@ -207,7 +207,6 @@ public partial class NavMapControl : MapGridControl // Find closest tracked entity in range var closestEntity = NetEntity.Invalid; - var closestCoords = new EntityCoordinates(); var closestDistance = float.PositiveInfinity; foreach ((var currentEntity, var blip) in TrackedEntities) @@ -221,7 +220,6 @@ public partial class NavMapControl : MapGridControl continue; closestEntity = currentEntity; - closestCoords = blip.Coordinates; closestDistance = currentDistance; } @@ -234,8 +232,7 @@ public partial class NavMapControl : MapGridControl else if (args.Function == EngineKeyFunctions.UIRightClick) { // Clear current selection with right click - if (TrackedEntitySelectedAction != null) - TrackedEntitySelectedAction.Invoke(null); + TrackedEntitySelectedAction?.Invoke(null); } } @@ -260,7 +257,7 @@ public partial class NavMapControl : MapGridControl { base.Draw(handle); - // Get the components necessary for drawing the navmap + // Get the components necessary for drawing the navmap _entManager.TryGetComponent(MapUid, out _navMap); _entManager.TryGetComponent(MapUid, out _grid); _entManager.TryGetComponent(MapUid, out _xform); @@ -318,7 +315,7 @@ public partial class NavMapControl : MapGridControl var area = new Box2(-WorldRange, -WorldRange, WorldRange + 1f, WorldRange + 1f).Translated(offset); - // Drawing lines can be rather expensive due to the number of neighbors that need to be checked in order + // Drawing lines can be rather expensive due to the number of neighbors that need to be checked in order // to figure out where they should be drawn. However, we don't *need* to do check these every frame. // Instead, lets periodically update where to draw each line and then store these points in a list. // Then we can just run through the list each frame and draw the lines without any extra computation. @@ -360,6 +357,41 @@ public partial class NavMapControl : MapGridControl } } + var airlockBuffer = Vector2.One * (MinimapScale / 2.25f) * 0.75f; + var airlockLines = new ValueList(); + var foobarVec = new Vector2(1, -1); + + foreach (var airlock in _navMap.Airlocks) + { + var position = airlock.Position - offset; + position = Scale(position with { Y = -position.Y }); + airlockLines.Add(position + airlockBuffer); + airlockLines.Add(position - airlockBuffer * foobarVec); + + airlockLines.Add(position + airlockBuffer); + airlockLines.Add(position + airlockBuffer * foobarVec); + + airlockLines.Add(position - airlockBuffer); + airlockLines.Add(position + airlockBuffer * foobarVec); + + airlockLines.Add(position - airlockBuffer); + airlockLines.Add(position - airlockBuffer * foobarVec); + + airlockLines.Add(position + airlockBuffer * -Vector2.UnitY); + airlockLines.Add(position - airlockBuffer * -Vector2.UnitY); + } + + if (airlockLines.Count > 0) + { + if (!_sRGBLookUp.TryGetValue(WallColor, out var sRGB)) + { + sRGB = Color.ToSrgb(WallColor); + _sRGBLookUp[WallColor] = sRGB; + } + + handle.DrawPrimitives(DrawPrimitiveTopology.LineList, airlockLines.Span, sRGB); + } + if (PostWallDrawingAction != null) PostWallDrawingAction.Invoke(handle); diff --git a/Content.Server/Pinpointer/NavMapSystem.cs b/Content.Server/Pinpointer/NavMapSystem.cs index 567175abc5..f2a23693a8 100644 --- a/Content.Server/Pinpointer/NavMapSystem.cs +++ b/Content.Server/Pinpointer/NavMapSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Pinpointer; using Content.Shared.Tag; +using Robust.Server.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Map.Components; using Robust.Shared.Physics; @@ -20,6 +21,7 @@ public sealed class NavMapSystem : SharedNavMapSystem [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly MapSystem _map = default!; private EntityQuery _physicsQuery; private EntityQuery _tagQuery; @@ -41,6 +43,9 @@ public sealed class NavMapSystem : SharedNavMapSystem SubscribeLocalEvent(OnNavMapBeaconStartup); SubscribeLocalEvent(OnNavMapBeaconAnchor); + SubscribeLocalEvent(OnNavMapDoorStartup); + SubscribeLocalEvent(OnNavMapDoorAnchor); + SubscribeLocalEvent(OnConfigureMessage); SubscribeLocalEvent(OnConfigurableMapInit); SubscribeLocalEvent(OnConfigurableExamined); @@ -63,6 +68,16 @@ public sealed class NavMapSystem : SharedNavMapSystem RefreshNavGrid(uid); } + private void OnNavMapDoorStartup(Entity ent, ref ComponentStartup args) + { + RefreshNavGrid(ent); + } + + private void OnNavMapDoorAnchor(Entity ent, ref AnchorStateChangedEvent args) + { + RefreshNavGrid(ent); + } + private void OnConfigureMessage(Entity ent, ref NavMapBeaconConfigureBuiMessage args) { if (args.Session.AttachedEntity is not { } user) @@ -190,6 +205,9 @@ public sealed class NavMapSystem : SharedNavMapSystem private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentGetState args) { + if (!TryComp(uid, out var mapGrid)) + return; + var data = new Dictionary(component.Chunks.Count); foreach (var (index, chunk) in component.Chunks) { @@ -222,11 +240,45 @@ public sealed class NavMapSystem : SharedNavMapSystem beacons.Add(new NavMapBeacon(beacon.Color, name, xform.LocalPosition)); } + var airlockQuery = EntityQueryEnumerator(); + var airlocks = new List(); + while (airlockQuery.MoveNext(out _, out _, out var xform)) + { + if (xform.GridUid != uid || !xform.Anchored) + continue; + + var pos = _map.TileIndicesFor(uid, mapGrid, xform.Coordinates); + var enumerator = _map.GetAnchoredEntitiesEnumerator(uid, mapGrid, pos); + + var wallPresent = false; + while (enumerator.MoveNext(out var ent)) + { + if (!_physicsQuery.TryGetComponent(ent, out var body) || + !body.CanCollide || + !body.Hard || + body.BodyType != BodyType.Static || + !_tags.HasTag(ent.Value, "Wall", _tagQuery) && + !_tags.HasTag(ent.Value, "Window", _tagQuery)) + { + continue; + } + + wallPresent = true; + break; + } + + if (wallPresent) + continue; + + airlocks.Add(new NavMapAirlock(xform.LocalPosition)); + } + // TODO: Diffs args.State = new NavMapComponentState() { TileData = data, Beacons = beacons, + Airlocks = airlocks }; } @@ -286,8 +338,8 @@ public sealed class NavMapSystem : SharedNavMapSystem !body.CanCollide || !body.Hard || body.BodyType != BodyType.Static || - (!_tags.HasTag(ent.Value, "Wall", _tagQuery) && - !_tags.HasTag(ent.Value, "Window", _tagQuery))) + !_tags.HasTag(ent.Value, "Wall", _tagQuery) && + !_tags.HasTag(ent.Value, "Window", _tagQuery)) { continue; } diff --git a/Content.Shared/Pinpointer/NavMapComponent.cs b/Content.Shared/Pinpointer/NavMapComponent.cs index 57f2d004d5..8c9979ba25 100644 --- a/Content.Shared/Pinpointer/NavMapComponent.cs +++ b/Content.Shared/Pinpointer/NavMapComponent.cs @@ -16,6 +16,8 @@ public sealed partial class NavMapComponent : Component public readonly Dictionary Chunks = new(); [ViewVariables] public readonly List Beacons = new(); + + [ViewVariables] public readonly List Airlocks = new(); } public sealed class NavMapChunk diff --git a/Content.Shared/Pinpointer/NavMapDoorComponent.cs b/Content.Shared/Pinpointer/NavMapDoorComponent.cs new file mode 100644 index 0000000000..914f88c4f9 --- /dev/null +++ b/Content.Shared/Pinpointer/NavMapDoorComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Pinpointer; + +/// +/// This is used for objects which appear as doors on the navmap. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedNavMapSystem))] +public sealed partial class NavMapDoorComponent : Component +{ + +} diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 3f01934a24..17f86ac7e6 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -37,8 +37,13 @@ public abstract class SharedNavMapSystem : EntitySystem public Dictionary TileData = new(); public List Beacons = new(); + + public List Airlocks = new(); } [Serializable, NetSerializable] public readonly record struct NavMapBeacon(Color Color, string Text, Vector2 Position); + + [Serializable, NetSerializable] + public readonly record struct NavMapAirlock(Vector2 Position); } diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index bbb85f335e..22770ffc18 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -64,6 +64,7 @@ - type: Weldable time: 3 - type: Airlock + - type: NavMapDoor - type: DoorBolt - type: Appearance - type: WiresVisuals diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml index 2ed1c60d37..d799558df7 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml @@ -57,6 +57,7 @@ - type: Weldable time: 10 - type: Airlock + - type: NavMapDoor - type: DoorBolt - type: AccessReader - type: Appearance diff --git a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml index 176eaf7651..f661b507bd 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml @@ -57,10 +57,17 @@ mode: NoSprite - type: Occluder +- type: entity + parent: BaseMaterialDoor + id: BaseMaterialDoorNavMap + abstract: true + components: + - type: NavMapDoor + - type: entity id: MetalDoor name: metal door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap components: - type: Construction graph: DoorGraph @@ -69,7 +76,7 @@ - type: entity id: WoodDoor name: wooden door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap description: A door, where will it lead? components: - type: Sprite @@ -106,7 +113,7 @@ - type: entity id: PaperDoor name: paper door - parent: WoodDoor + parent: BaseMaterialDoorNavMap description: A door, where will it lead? components: - type: Sprite @@ -129,7 +136,7 @@ - type: entity id: PlasmaDoor name: plasma door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap description: A door, where will it lead? components: - type: Sprite @@ -155,7 +162,7 @@ - type: entity id: GoldDoor name: gold door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap description: A door, where will it lead? components: - type: Sprite @@ -170,7 +177,7 @@ - type: entity id: SilverDoor name: silver door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap description: A door, where will it lead? components: - type: Sprite @@ -185,7 +192,7 @@ - type: entity id: BananiumDoor name: bananium door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap description: A door, where will it lead? components: - type: Sprite @@ -205,7 +212,7 @@ - type: entity id: WebDoor name: web door - parent: BaseMaterialDoor + parent: BaseMaterialDoorNavMap description: A door, leading to the lands of the spiders... or a spaced room. components: - type: Sprite diff --git a/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml b/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml index cafaea3a22..154467213a 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/SecretDoor/secret_door.yml @@ -57,6 +57,10 @@ - type: ContainerFill containers: battery-container: [ PowerCellMedium ] + - type: Tag + tags: + - Structure + - Wall - type: ContainerContainer containers: battery-container: !type:Container diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml index 876e4d0ef4..f4a5debeac 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml @@ -32,6 +32,7 @@ - type: ContainerContainer containers: board: !type:Container + - type: NavMapDoor - type: Door openDrawDepth: BlastDoors closedDrawDepth: BlastDoors diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml index 60d03e0091..e3fab04da8 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml @@ -112,6 +112,7 @@ openUnlitVisible: true # needed so that windoors will close regardless of whether there are people in it; it doesn't crush after all safety: false + - type: NavMapDoor - type: DoorBolt - type: Electrified enabled: false