2023-12-28 19:02:21 -05:00
using Content.Server.Administration.Logs ;
2023-04-13 21:13:24 +10:00
using Content.Server.Station.Systems ;
2023-09-16 18:11:47 +10:00
using Content.Server.Warps ;
2023-12-28 19:02:21 -05:00
using Content.Shared.Database ;
using Content.Shared.Examine ;
2023-04-13 16:21:24 +10:00
using Content.Shared.Pinpointer ;
using Content.Shared.Tag ;
2024-01-11 08:14:20 -05:00
using Robust.Server.GameObjects ;
2023-04-13 16:21:24 +10:00
using Robust.Shared.GameStates ;
using Robust.Shared.Map.Components ;
using Robust.Shared.Physics ;
using Robust.Shared.Physics.Components ;
namespace Content.Server.Pinpointer ;
/// <summary>
/// Handles data to be used for in-grid map displays.
/// </summary>
public sealed class NavMapSystem : SharedNavMapSystem
{
2023-12-28 19:02:21 -05:00
[Dependency] private readonly IAdminLogManager _adminLog = default ! ;
[Dependency] private readonly SharedAppearanceSystem _appearance = default ! ;
2023-04-13 16:21:24 +10:00
[Dependency] private readonly TagSystem _tags = default ! ;
2024-01-11 08:14:20 -05:00
[Dependency] private readonly MapSystem _map = default ! ;
2023-04-13 16:21:24 +10:00
2023-09-16 18:11:47 +10:00
private EntityQuery < PhysicsComponent > _physicsQuery ;
private EntityQuery < TagComponent > _tagQuery ;
2023-04-13 16:21:24 +10:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2023-09-16 18:11:47 +10:00
_physicsQuery = GetEntityQuery < PhysicsComponent > ( ) ;
_tagQuery = GetEntityQuery < TagComponent > ( ) ;
2023-04-13 16:21:24 +10:00
SubscribeLocalEvent < AnchorStateChangedEvent > ( OnAnchorChange ) ;
SubscribeLocalEvent < ReAnchorEvent > ( OnReAnchor ) ;
2023-09-16 18:11:47 +10:00
SubscribeLocalEvent < StationGridAddedEvent > ( OnStationInit ) ;
SubscribeLocalEvent < NavMapComponent , ComponentStartup > ( OnNavMapStartup ) ;
2023-04-13 16:21:24 +10:00
SubscribeLocalEvent < NavMapComponent , ComponentGetState > ( OnGetState ) ;
Power monitoring console overhaul (#20927)
* Prototyping whole station wire map
* More prototyping
* Added icons for the different power distributors and toggleable cable displays
* Power cable layouts are now only sent to the client when the power monitor is open
* UI prototyping
* Power monitors can now see the sprites of distant entities, long entity names are truncated
* Updated how network devices are added to the player's PVS
* More feature prototypes
* Added source / load symbols
* Final prototype! Time to actually code it properly...
* Start of code clean up
* Continuing code clean up
* Fixed UI appearance
* Code clean up complete
* Removed unnecessary changes
* Updated how power values are calculated, added UI warnings for power sinks and power net checks
* Updated how power values are calculated again, added support for portable generators
* Removed unnecessary files
* Map beacons start toggled off, console map now works outside the station, fixed substation icon
* Made some of Sloth's requested changes. Power distributors don't blink anymore, unless selected
* Moved a number of static variables in PowerMonitoringHelper to sensible places in the main files. Added a NavMapTrackableComponent so that you can specify how individual entities appear on the navmap
* Updated the colors/positions of HV cables and SMESes to improve contrast
* Fixed SMES color in map legend
* Partially fixed auto-scrolling on device selection, made sublists alphabetical
* Changed how auto-scroll is handled
* Changed the font color of the console warning messages
* Reduced the font size of beacon labels
* Added the station name to the console
* Organized references
* Removed unwanted changes to RobustToolbox
* Fix merge conflict
* Fix merge conflict, maybe
* Fix merge conflict
* Updated outdated reference
* Fixed portable_generator.yml
* Implemented a number of requested changes, move bit masks to a shared component
* Navigate listings via the navmap
* First attempt at improving efficiency
* Second attempt at optimization, entity grouping added for solar panels
* Finished solar panel entity joining
* Finished major revisions, code clean up needed
* Finializing optimizations
* Made requested changes
* Bug fix, removed obsolete code
* Bug fixes
* Bug fixes
* STarted revisions
* Further revisions
* More revision
* Finalizing revisions. Need to make RT PR
* Code tidying
* More code tidying
* Trying to avoid merge conflicts
* Trying to avoid merge conflicts
* Removed use of PVS
* Improving efficiency
* Addressed a bunch of outstanding issues
* Clear old data on console refresh
* UI adjustments
* Made node comparison more robust. More devices can be combined into one entry
* Added missing component 'dirty'
2023-12-24 00:07:41 -06:00
SubscribeLocalEvent < GridSplitEvent > ( OnNavMapSplit ) ;
2023-09-16 18:11:47 +10:00
SubscribeLocalEvent < NavMapBeaconComponent , ComponentStartup > ( OnNavMapBeaconStartup ) ;
SubscribeLocalEvent < NavMapBeaconComponent , AnchorStateChangedEvent > ( OnNavMapBeaconAnchor ) ;
2023-12-28 19:02:21 -05:00
2024-01-11 08:14:20 -05:00
SubscribeLocalEvent < NavMapDoorComponent , ComponentStartup > ( OnNavMapDoorStartup ) ;
SubscribeLocalEvent < NavMapDoorComponent , AnchorStateChangedEvent > ( OnNavMapDoorAnchor ) ;
2023-12-28 19:02:21 -05:00
SubscribeLocalEvent < ConfigurableNavMapBeaconComponent , NavMapBeaconConfigureBuiMessage > ( OnConfigureMessage ) ;
SubscribeLocalEvent < ConfigurableNavMapBeaconComponent , MapInitEvent > ( OnConfigurableMapInit ) ;
SubscribeLocalEvent < ConfigurableNavMapBeaconComponent , ExaminedEvent > ( OnConfigurableExamined ) ;
2023-04-13 21:13:24 +10:00
}
private void OnStationInit ( StationGridAddedEvent ev )
{
var comp = EnsureComp < NavMapComponent > ( ev . GridId ) ;
2024-03-19 23:27:02 -04:00
RefreshGrid ( ev . GridId , comp , Comp < MapGridComponent > ( ev . GridId ) ) ;
2023-09-16 18:11:47 +10:00
}
private void OnNavMapBeaconStartup ( EntityUid uid , NavMapBeaconComponent component , ComponentStartup args )
{
RefreshNavGrid ( uid ) ;
}
private void OnNavMapBeaconAnchor ( EntityUid uid , NavMapBeaconComponent component , ref AnchorStateChangedEvent args )
{
2023-12-28 19:02:21 -05:00
UpdateBeaconEnabledVisuals ( ( uid , component ) ) ;
2023-09-16 18:11:47 +10:00
RefreshNavGrid ( uid ) ;
}
2024-01-11 08:14:20 -05:00
private void OnNavMapDoorStartup ( Entity < NavMapDoorComponent > ent , ref ComponentStartup args )
{
RefreshNavGrid ( ent ) ;
}
private void OnNavMapDoorAnchor ( Entity < NavMapDoorComponent > ent , ref AnchorStateChangedEvent args )
{
RefreshNavGrid ( ent ) ;
}
2023-12-28 19:02:21 -05:00
private void OnConfigureMessage ( Entity < ConfigurableNavMapBeaconComponent > ent , ref NavMapBeaconConfigureBuiMessage args )
{
if ( args . Session . AttachedEntity is not { } user )
return ;
if ( ! TryComp < NavMapBeaconComponent > ( ent , out var navMap ) )
return ;
if ( navMap . Text = = args . Text & &
navMap . Color = = args . Color & &
navMap . Enabled = = args . Enabled )
return ;
_adminLog . Add ( LogType . Action , LogImpact . Medium ,
$"{ToPrettyString(user):player} configured NavMapBeacon \'{ToPrettyString(ent):entity}\' with text \'{args.Text}\', color {args.Color.ToHexNoAlpha()}, and {(args.Enabled ? " enabled " : " disabled ")} it." ) ;
if ( TryComp < WarpPointComponent > ( ent , out var warpPoint ) )
{
warpPoint . Location = args . Text ;
}
navMap . Text = args . Text ;
navMap . Color = args . Color ;
navMap . Enabled = args . Enabled ;
UpdateBeaconEnabledVisuals ( ( ent , navMap ) ) ;
Dirty ( ent , navMap ) ;
RefreshNavGrid ( ent ) ;
}
private void OnConfigurableMapInit ( Entity < ConfigurableNavMapBeaconComponent > ent , ref MapInitEvent args )
{
if ( ! TryComp < NavMapBeaconComponent > ( ent , out var navMap ) )
return ;
// We set this on mapinit just in case the text was edited via VV or something.
if ( TryComp < WarpPointComponent > ( ent , out var warpPoint ) )
{
warpPoint . Location = navMap . Text ;
}
UpdateBeaconEnabledVisuals ( ( ent , navMap ) ) ;
}
private void OnConfigurableExamined ( Entity < ConfigurableNavMapBeaconComponent > ent , ref ExaminedEvent args )
{
if ( ! args . IsInDetailsRange | | ! TryComp < NavMapBeaconComponent > ( ent , out var navMap ) )
return ;
args . PushMarkup ( Loc . GetString ( "nav-beacon-examine-text" ,
( "enabled" , navMap . Enabled ) ,
( "color" , navMap . Color . ToHexNoAlpha ( ) ) ,
( "label" , navMap . Text ? ? string . Empty ) ) ) ;
}
private void UpdateBeaconEnabledVisuals ( Entity < NavMapBeaconComponent > ent )
{
_appearance . SetData ( ent , NavMapBeaconVisuals . Enabled , ent . Comp . Enabled & & Transform ( ent ) . Anchored ) ;
}
2023-09-16 18:11:47 +10:00
/// <summary>
/// Refreshes the grid for the corresponding beacon.
/// </summary>
/// <param name="uid"></param>
private void RefreshNavGrid ( EntityUid uid )
{
var xform = Transform ( uid ) ;
2023-12-28 19:02:21 -05:00
if ( ! TryComp < NavMapComponent > ( xform . GridUid , out var navMap ) )
2023-09-16 18:11:47 +10:00
return ;
Dirty ( xform . GridUid . Value , navMap ) ;
}
private bool CanBeacon ( EntityUid uid , TransformComponent ? xform = null )
{
if ( ! Resolve ( uid , ref xform ) )
return false ;
return xform . GridUid ! = null & & xform . Anchored ;
}
private void OnNavMapStartup ( EntityUid uid , NavMapComponent component , ComponentStartup args )
{
if ( ! TryComp < MapGridComponent > ( uid , out var grid ) )
return ;
2024-03-19 23:27:02 -04:00
RefreshGrid ( uid , component , grid ) ;
2023-04-13 16:21:24 +10:00
}
Power monitoring console overhaul (#20927)
* Prototyping whole station wire map
* More prototyping
* Added icons for the different power distributors and toggleable cable displays
* Power cable layouts are now only sent to the client when the power monitor is open
* UI prototyping
* Power monitors can now see the sprites of distant entities, long entity names are truncated
* Updated how network devices are added to the player's PVS
* More feature prototypes
* Added source / load symbols
* Final prototype! Time to actually code it properly...
* Start of code clean up
* Continuing code clean up
* Fixed UI appearance
* Code clean up complete
* Removed unnecessary changes
* Updated how power values are calculated, added UI warnings for power sinks and power net checks
* Updated how power values are calculated again, added support for portable generators
* Removed unnecessary files
* Map beacons start toggled off, console map now works outside the station, fixed substation icon
* Made some of Sloth's requested changes. Power distributors don't blink anymore, unless selected
* Moved a number of static variables in PowerMonitoringHelper to sensible places in the main files. Added a NavMapTrackableComponent so that you can specify how individual entities appear on the navmap
* Updated the colors/positions of HV cables and SMESes to improve contrast
* Fixed SMES color in map legend
* Partially fixed auto-scrolling on device selection, made sublists alphabetical
* Changed how auto-scroll is handled
* Changed the font color of the console warning messages
* Reduced the font size of beacon labels
* Added the station name to the console
* Organized references
* Removed unwanted changes to RobustToolbox
* Fix merge conflict
* Fix merge conflict, maybe
* Fix merge conflict
* Updated outdated reference
* Fixed portable_generator.yml
* Implemented a number of requested changes, move bit masks to a shared component
* Navigate listings via the navmap
* First attempt at improving efficiency
* Second attempt at optimization, entity grouping added for solar panels
* Finished solar panel entity joining
* Finished major revisions, code clean up needed
* Finializing optimizations
* Made requested changes
* Bug fix, removed obsolete code
* Bug fixes
* Bug fixes
* STarted revisions
* Further revisions
* More revision
* Finalizing revisions. Need to make RT PR
* Code tidying
* More code tidying
* Trying to avoid merge conflicts
* Trying to avoid merge conflicts
* Removed use of PVS
* Improving efficiency
* Addressed a bunch of outstanding issues
* Clear old data on console refresh
* UI adjustments
* Made node comparison more robust. More devices can be combined into one entry
* Added missing component 'dirty'
2023-12-24 00:07:41 -06:00
private void OnNavMapSplit ( ref GridSplitEvent args )
2023-04-13 16:21:24 +10:00
{
2024-01-06 16:06:52 -05:00
if ( ! TryComp ( args . Grid , out NavMapComponent ? comp ) )
return ;
2023-04-13 16:21:24 +10:00
var gridQuery = GetEntityQuery < MapGridComponent > ( ) ;
foreach ( var grid in args . NewGrids )
{
var newComp = EnsureComp < NavMapComponent > ( grid ) ;
2024-03-19 23:27:02 -04:00
RefreshGrid ( grid , newComp , gridQuery . GetComponent ( grid ) ) ;
2023-04-13 16:21:24 +10:00
}
2024-03-19 23:27:02 -04:00
RefreshGrid ( args . Grid , comp , gridQuery . GetComponent ( args . Grid ) ) ;
2023-04-13 16:21:24 +10:00
}
2024-03-19 23:27:02 -04:00
private void RefreshGrid ( EntityUid uid , NavMapComponent component , MapGridComponent grid )
2023-04-13 16:21:24 +10:00
{
component . Chunks . Clear ( ) ;
var tiles = grid . GetAllTilesEnumerator ( ) ;
while ( tiles . MoveNext ( out var tile ) )
{
var chunkOrigin = SharedMapSystem . GetChunkIndices ( tile . Value . GridIndices , ChunkSize ) ;
if ( ! component . Chunks . TryGetValue ( chunkOrigin , out var chunk ) )
{
chunk = new NavMapChunk ( chunkOrigin ) ;
2023-04-13 21:13:24 +10:00
component . Chunks [ chunkOrigin ] = chunk ;
2023-04-13 16:21:24 +10:00
}
2024-03-19 23:27:02 -04:00
RefreshTile ( uid , grid , component , chunk , tile . Value . GridIndices ) ;
2023-04-13 16:21:24 +10:00
}
}
private void OnGetState ( EntityUid uid , NavMapComponent component , ref ComponentGetState args )
{
2024-01-11 08:14:20 -05:00
if ( ! TryComp < MapGridComponent > ( uid , out var mapGrid ) )
return ;
2023-04-13 16:21:24 +10:00
var data = new Dictionary < Vector2i , int > ( component . Chunks . Count ) ;
foreach ( var ( index , chunk ) in component . Chunks )
{
data . Add ( index , chunk . TileData ) ;
}
2023-09-16 18:11:47 +10:00
var beaconQuery = AllEntityQuery < NavMapBeaconComponent , TransformComponent > ( ) ;
var beacons = new List < NavMapBeacon > ( ) ;
while ( beaconQuery . MoveNext ( out var beaconUid , out var beacon , out var xform ) )
{
2023-09-23 20:15:05 +01:00
if ( ! beacon . Enabled | | xform . GridUid ! = uid | | ! CanBeacon ( beaconUid , xform ) )
2023-09-16 18:11:47 +10:00
continue ;
// TODO: Make warp points use metadata name instead.
string? name = beacon . Text ;
2024-01-20 09:59:41 +01:00
if ( string . IsNullOrEmpty ( name ) )
2023-09-16 18:11:47 +10:00
{
if ( TryComp < WarpPointComponent > ( beaconUid , out var warpPoint ) & & warpPoint . Location ! = null )
{
name = warpPoint . Location ;
}
else
{
name = MetaData ( beaconUid ) . EntityName ;
}
}
beacons . Add ( new NavMapBeacon ( beacon . Color , name , xform . LocalPosition ) ) ;
}
2024-01-11 08:14:20 -05:00
var airlockQuery = EntityQueryEnumerator < NavMapDoorComponent , TransformComponent > ( ) ;
var airlocks = new List < NavMapAirlock > ( ) ;
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 ) ) ;
}
2023-04-13 16:21:24 +10:00
// TODO: Diffs
args . State = new NavMapComponentState ( )
{
TileData = data ,
2023-09-16 18:11:47 +10:00
Beacons = beacons ,
2024-01-11 08:14:20 -05:00
Airlocks = airlocks
2023-04-13 16:21:24 +10:00
} ;
}
private void OnReAnchor ( ref ReAnchorEvent ev )
{
if ( TryComp < MapGridComponent > ( ev . OldGrid , out var oldGrid ) & &
TryComp < NavMapComponent > ( ev . OldGrid , out var navMap ) )
{
var chunkOrigin = SharedMapSystem . GetChunkIndices ( ev . TilePos , ChunkSize ) ;
if ( navMap . Chunks . TryGetValue ( chunkOrigin , out var chunk ) )
{
2024-03-19 23:27:02 -04:00
RefreshTile ( ev . OldGrid , oldGrid , navMap , chunk , ev . TilePos ) ;
2023-04-13 16:21:24 +10:00
}
}
HandleAnchor ( ev . Xform ) ;
}
private void OnAnchorChange ( ref AnchorStateChangedEvent ev )
{
HandleAnchor ( ev . Transform ) ;
}
private void HandleAnchor ( TransformComponent xform )
{
if ( ! TryComp < NavMapComponent > ( xform . GridUid , out var navMap ) | |
! TryComp < MapGridComponent > ( xform . GridUid , out var grid ) )
return ;
var tile = grid . LocalToTile ( xform . Coordinates ) ;
var chunkOrigin = SharedMapSystem . GetChunkIndices ( tile , ChunkSize ) ;
if ( ! navMap . Chunks . TryGetValue ( chunkOrigin , out var chunk ) )
{
chunk = new NavMapChunk ( chunkOrigin ) ;
navMap . Chunks [ chunkOrigin ] = chunk ;
}
2024-03-19 23:27:02 -04:00
RefreshTile ( xform . GridUid . Value , grid , navMap , chunk , tile ) ;
2023-04-13 16:21:24 +10:00
}
2024-03-19 23:27:02 -04:00
private void RefreshTile ( EntityUid uid , MapGridComponent grid , NavMapComponent component , NavMapChunk chunk , Vector2i tile )
2023-04-13 16:21:24 +10:00
{
var relative = SharedMapSystem . GetChunkRelative ( tile , ChunkSize ) ;
var existing = chunk . TileData ;
var flag = GetFlag ( relative ) ;
chunk . TileData & = ~ flag ;
var enumerator = grid . GetAnchoredEntitiesEnumerator ( tile ) ;
// TODO: Use something to get convex poly.
while ( enumerator . MoveNext ( out var ent ) )
{
2023-09-16 18:11:47 +10:00
if ( ! _physicsQuery . TryGetComponent ( ent , out var body ) | |
2023-04-13 16:21:24 +10:00
! body . CanCollide | |
! body . Hard | |
body . BodyType ! = BodyType . Static | |
2024-01-11 08:14:20 -05:00
! _tags . HasTag ( ent . Value , "Wall" , _tagQuery ) & &
! _tags . HasTag ( ent . Value , "Window" , _tagQuery ) )
2023-04-13 16:21:24 +10:00
{
continue ;
}
chunk . TileData | = flag ;
break ;
}
if ( chunk . TileData = = 0 )
{
component . Chunks . Remove ( chunk . Origin ) ;
}
if ( existing = = chunk . TileData )
return ;
2024-03-19 23:27:02 -04:00
Dirty ( uid , component ) ;
2023-04-13 16:21:24 +10:00
}
2023-09-23 20:15:05 +01:00
/// <summary>
/// Sets the beacon's Enabled field and refreshes the grid.
/// </summary>
public void SetBeaconEnabled ( EntityUid uid , bool enabled , NavMapBeaconComponent ? comp = null )
{
if ( ! Resolve ( uid , ref comp ) | | comp . Enabled = = enabled )
return ;
comp . Enabled = enabled ;
2023-12-28 19:02:21 -05:00
UpdateBeaconEnabledVisuals ( ( uid , comp ) ) ;
Dirty ( uid , comp ) ;
2023-09-23 20:15:05 +01:00
RefreshNavGrid ( uid ) ;
}
/// <summary>
/// Toggles the beacon's Enabled field and refreshes the grid.
/// </summary>
public void ToggleBeacon ( EntityUid uid , NavMapBeaconComponent ? comp = null )
{
if ( ! Resolve ( uid , ref comp ) )
return ;
SetBeaconEnabled ( uid , ! comp . Enabled , comp ) ;
}
2023-04-13 16:21:24 +10:00
}