2022-06-03 21:37:35 +10:00
using System.Globalization ;
2022-07-01 13:40:36 -07:00
using System.Linq ;
2022-06-03 21:37:35 +10:00
using Content.Server.Access.Systems ;
2022-08-07 20:21:56 -03:00
using Content.Server.Administration.Logs ;
2022-06-03 21:37:35 +10:00
using Content.Server.AlertLevel ;
using Content.Server.Chat ;
2022-06-23 20:11:03 +10:00
using Content.Server.Chat.Systems ;
2022-07-01 13:40:36 -07:00
using Content.Server.Interaction ;
2022-06-03 21:37:35 +10:00
using Content.Server.Popups ;
using Content.Server.RoundEnd ;
2022-06-26 15:20:45 +10:00
using Content.Server.Shuttles.Systems ;
2022-06-03 21:37:35 +10:00
using Content.Server.Station.Systems ;
using Content.Shared.Access.Components ;
using Content.Shared.Access.Systems ;
2022-07-01 13:40:36 -07:00
using Content.Shared.CCVar ;
2022-06-03 21:37:35 +10:00
using Content.Shared.Communications ;
2022-08-07 20:21:56 -03:00
using Content.Shared.Database ;
2023-02-19 01:03:06 +00:00
using Content.Shared.Emag.Components ;
2022-07-01 13:40:36 -07:00
using Content.Shared.Examine ;
2022-07-09 02:09:52 -07:00
using Content.Shared.Popups ;
2022-07-01 13:40:36 -07:00
using Robust.Server.GameObjects ;
using Robust.Shared.Configuration ;
2022-06-03 21:37:35 +10:00
using Robust.Shared.Player ;
namespace Content.Server.Communications
{
public sealed class CommunicationsConsoleSystem : EntitySystem
{
2022-06-26 15:20:45 +10:00
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default ! ;
2022-07-01 13:40:36 -07:00
[Dependency] private readonly InteractionSystem _interaction = default ! ;
2022-06-03 21:37:35 +10:00
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default ! ;
2022-06-26 15:20:45 +10:00
[Dependency] private readonly ChatSystem _chatSystem = default ! ;
2023-03-24 12:54:41 +11:00
[Dependency] private readonly EmergencyShuttleSystem _emergency = default ! ;
2022-06-03 21:37:35 +10:00
[Dependency] private readonly IdCardSystem _idCardSystem = default ! ;
[Dependency] private readonly PopupSystem _popupSystem = default ! ;
2022-06-26 15:20:45 +10:00
[Dependency] private readonly RoundEndSystem _roundEndSystem = default ! ;
[Dependency] private readonly StationSystem _stationSystem = default ! ;
2022-07-01 13:40:36 -07:00
[Dependency] private readonly IConfigurationManager _cfg = default ! ;
2022-08-07 20:21:56 -03:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2023-07-08 09:02:17 -07:00
[Dependency] private readonly UserInterfaceSystem _uiSystem = default ! ;
2022-06-03 21:37:35 +10:00
private const int MaxMessageLength = 256 ;
2023-04-14 12:57:47 -07:00
private const int MaxMessageNewlines = 2 ;
2022-07-01 13:40:36 -07:00
private const float UIUpdateInterval = 5.0f ;
2022-06-03 21:37:35 +10:00
public override void Initialize ( )
{
// All events that refresh the BUI
SubscribeLocalEvent < AlertLevelChangedEvent > ( OnAlertLevelChanged ) ;
2023-07-08 09:02:17 -07:00
SubscribeLocalEvent < CommunicationsConsoleComponent , ComponentInit > ( ( uid , comp , _ ) = > UpdateCommsConsoleInterface ( uid , comp ) ) ;
2022-06-03 21:37:35 +10:00
SubscribeLocalEvent < RoundEndSystemChangedEvent > ( _ = > OnGenericBroadcastEvent ( ) ) ;
SubscribeLocalEvent < AlertLevelDelayFinishedEvent > ( _ = > OnGenericBroadcastEvent ( ) ) ;
// Messages from the BUI
SubscribeLocalEvent < CommunicationsConsoleComponent , CommunicationsConsoleSelectAlertLevelMessage > ( OnSelectAlertLevelMessage ) ;
SubscribeLocalEvent < CommunicationsConsoleComponent , CommunicationsConsoleAnnounceMessage > ( OnAnnounceMessage ) ;
SubscribeLocalEvent < CommunicationsConsoleComponent , CommunicationsConsoleCallEmergencyShuttleMessage > ( OnCallShuttleMessage ) ;
SubscribeLocalEvent < CommunicationsConsoleComponent , CommunicationsConsoleRecallEmergencyShuttleMessage > ( OnRecallShuttleMessage ) ;
}
public override void Update ( float frameTime )
{
2023-07-08 09:02:17 -07:00
var query = EntityQueryEnumerator < CommunicationsConsoleComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var comp ) )
2022-06-03 21:37:35 +10:00
{
2022-07-01 13:40:36 -07:00
// TODO refresh the UI in a less horrible way
if ( comp . AnnouncementCooldownRemaining > = 0f )
2022-06-03 21:37:35 +10:00
{
2022-07-01 13:40:36 -07:00
comp . AnnouncementCooldownRemaining - = frameTime ;
2022-06-03 21:37:35 +10:00
}
2022-07-01 13:40:36 -07:00
comp . UIUpdateAccumulator + = frameTime ;
if ( comp . UIUpdateAccumulator < UIUpdateInterval )
continue ;
comp . UIUpdateAccumulator - = UIUpdateInterval ;
2023-07-08 09:02:17 -07:00
if ( comp . UserInterface is { } ui & & ui . SubscribedSessions . Count > 0 )
UpdateCommsConsoleInterface ( uid , comp ) ;
2022-06-03 21:37:35 +10:00
}
base . Update ( frameTime ) ;
}
/// <summary>
/// Update the UI of every comms console.
/// </summary>
private void OnGenericBroadcastEvent ( )
{
2023-07-08 09:02:17 -07:00
var query = EntityQueryEnumerator < CommunicationsConsoleComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var comp ) )
2022-06-03 21:37:35 +10:00
{
2023-07-08 09:02:17 -07:00
UpdateCommsConsoleInterface ( uid , comp ) ;
2022-06-03 21:37:35 +10:00
}
}
/// <summary>
/// Updates all comms consoles belonging to the station that the alert level was set on
/// </summary>
/// <param name="args">Alert level changed event arguments</param>
private void OnAlertLevelChanged ( AlertLevelChangedEvent args )
{
2023-07-08 09:02:17 -07:00
var query = EntityQueryEnumerator < CommunicationsConsoleComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var comp ) )
2022-06-03 21:37:35 +10:00
{
2023-07-08 09:02:17 -07:00
var entStation = _stationSystem . GetOwningStation ( uid ) ;
2022-06-03 21:37:35 +10:00
if ( args . Station = = entStation )
2023-07-08 09:02:17 -07:00
UpdateCommsConsoleInterface ( uid , comp ) ;
2022-06-03 21:37:35 +10:00
}
}
2022-06-26 15:20:45 +10:00
/// <summary>
/// Updates the UI for all comms consoles.
/// </summary>
public void UpdateCommsConsoleInterface ( )
{
2023-07-08 09:02:17 -07:00
var query = EntityQueryEnumerator < CommunicationsConsoleComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var comp ) )
2022-06-26 15:20:45 +10:00
{
2023-07-08 09:02:17 -07:00
UpdateCommsConsoleInterface ( uid , comp ) ;
2022-06-26 15:20:45 +10:00
}
}
/// <summary>
/// Updates the UI for a particular comms console.
/// </summary>
/// <param name="comp"></param>
2023-07-08 09:02:17 -07:00
public void UpdateCommsConsoleInterface ( EntityUid uid , CommunicationsConsoleComponent comp )
2022-06-03 21:37:35 +10:00
{
var stationUid = _stationSystem . GetOwningStation ( uid ) ;
List < string > ? levels = null ;
string currentLevel = default ! ;
float currentDelay = 0 ;
if ( stationUid ! = null )
{
if ( TryComp ( stationUid . Value , out AlertLevelComponent ? alertComp ) & &
alertComp . AlertLevels ! = null )
{
if ( alertComp . IsSelectable )
{
levels = new ( ) ;
foreach ( var ( id , detail ) in alertComp . AlertLevels . Levels )
{
if ( detail . Selectable )
{
levels . Add ( id ) ;
}
}
}
currentLevel = alertComp . CurrentLevel ;
currentDelay = _alertLevelSystem . GetAlertLevelDelay ( stationUid . Value , alertComp ) ;
}
}
2023-07-08 09:02:17 -07:00
if ( comp . UserInterface is not null )
UserInterfaceSystem . SetUiState ( comp . UserInterface , new CommunicationsConsoleInterfaceState (
2022-06-03 21:37:35 +10:00
CanAnnounce ( comp ) ,
2022-07-01 13:40:36 -07:00
CanCallOrRecall ( comp ) ,
2022-06-03 21:37:35 +10:00
levels ,
currentLevel ,
currentDelay ,
_roundEndSystem . ExpectedCountdownEnd
2023-07-08 09:02:17 -07:00
) ) ;
2022-06-03 21:37:35 +10:00
}
2023-07-08 09:02:17 -07:00
private static bool CanAnnounce ( CommunicationsConsoleComponent comp )
2022-06-03 21:37:35 +10:00
{
return comp . AnnouncementCooldownRemaining < = 0f ;
}
private bool CanUse ( EntityUid user , EntityUid console )
{
2022-07-01 13:40:36 -07:00
// This shouldn't technically be possible because of BUI but don't trust client.
if ( ! _interaction . InRangeUnobstructed ( console , user ) )
return false ;
2023-02-19 01:03:06 +00:00
if ( TryComp < AccessReaderComponent > ( console , out var accessReaderComponent ) & & ! HasComp < EmaggedComponent > ( console ) )
2022-06-03 21:37:35 +10:00
{
return _accessReaderSystem . IsAllowed ( user , accessReaderComponent ) ;
}
return true ;
}
2022-07-01 13:40:36 -07:00
private bool CanCallOrRecall ( CommunicationsConsoleComponent comp )
2022-06-03 21:37:35 +10:00
{
2022-07-01 13:40:36 -07:00
// Defer to what the round end system thinks we should be able to do.
2023-03-24 12:54:41 +11:00
if ( _emergency . EmergencyShuttleArrived | | ! _roundEndSystem . CanCallOrRecall ( ) )
2022-07-01 13:40:36 -07:00
return false ;
// Calling shuttle checks
if ( _roundEndSystem . ExpectedCountdownEnd is null )
return comp . CanCallShuttle ;
2022-06-26 15:20:45 +10:00
2022-07-01 13:40:36 -07:00
// Recalling shuttle checks
var recallThreshold = _cfg . GetCVar ( CCVars . EmergencyRecallTurningPoint ) ;
// shouldn't really be happening if we got here
if ( _roundEndSystem . ShuttleTimeLeft is not { } left
| | _roundEndSystem . ExpectedShuttleLength is not { } expected )
return false ;
return ! ( left . TotalSeconds / expected . TotalSeconds < recallThreshold ) ;
2022-06-03 21:37:35 +10:00
}
private void OnSelectAlertLevelMessage ( EntityUid uid , CommunicationsConsoleComponent comp , CommunicationsConsoleSelectAlertLevelMessage message )
{
2023-07-08 09:02:17 -07:00
if ( message . Session . AttachedEntity is not { Valid : true } mob ) return ;
2022-06-03 21:37:35 +10:00
if ( ! CanUse ( mob , uid ) )
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupCursor ( Loc . GetString ( "comms-console-permission-denied" ) , message . Session , PopupType . Medium ) ;
2022-06-03 21:37:35 +10:00
return ;
}
var stationUid = _stationSystem . GetOwningStation ( uid ) ;
if ( stationUid ! = null )
{
_alertLevelSystem . SetLevel ( stationUid . Value , message . Level , true , true ) ;
}
}
private void OnAnnounceMessage ( EntityUid uid , CommunicationsConsoleComponent comp ,
CommunicationsConsoleAnnounceMessage message )
{
2023-07-08 09:02:17 -07:00
var msgWords = message . Message . Trim ( ) ;
var msgChars = ( msgWords . Length < = MaxMessageLength ? msgWords : $"{msgWords[0..MaxMessageLength]}..." ) . ToCharArray ( ) ;
2023-04-14 12:57:47 -07:00
var newlines = 0 ;
for ( var i = 0 ; i < msgChars . Length ; i + + )
{
if ( msgChars [ i ] ! = '\n' )
continue ;
if ( newlines > = MaxMessageNewlines )
msgChars [ i ] = ' ' ;
newlines + + ;
}
var msg = new string ( msgChars ) ;
2022-06-03 21:37:35 +10:00
var author = Loc . GetString ( "comms-console-announcement-unknown-sender" ) ;
2023-07-08 09:02:17 -07:00
if ( message . Session . AttachedEntity is { Valid : true } mob )
2022-06-03 21:37:35 +10:00
{
if ( ! CanAnnounce ( comp ) )
{
return ;
}
if ( ! CanUse ( mob , uid ) )
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( "comms-console-permission-denied" ) , uid , message . Session ) ;
2022-06-03 21:37:35 +10:00
return ;
}
if ( _idCardSystem . TryFindIdCard ( mob , out var id ) )
{
author = $"{id.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(id.JobTitle ?? string.Empty)})" . Trim ( ) ;
}
}
comp . AnnouncementCooldownRemaining = comp . DelayBetweenAnnouncements ;
2023-07-08 09:02:17 -07:00
UpdateCommsConsoleInterface ( uid , comp ) ;
2022-06-03 21:37:35 +10:00
// allow admemes with vv
Loc . TryGetString ( comp . AnnouncementDisplayName , out var title ) ;
title ? ? = comp . AnnouncementDisplayName ;
msg + = "\n" + Loc . GetString ( "comms-console-announcement-sent-by" ) + " " + author ;
if ( comp . AnnounceGlobal )
{
2022-07-15 12:16:41 +03:00
_chatSystem . DispatchGlobalAnnouncement ( msg , title , announcementSound : comp . AnnouncementSound , colorOverride : comp . AnnouncementColor ) ;
2022-08-07 20:21:56 -03:00
if ( message . Session . AttachedEntity ! = null )
_adminLogger . Add ( LogType . Chat , LogImpact . Low , $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following global announcement: {msg}" ) ;
2022-06-04 19:09:04 +10:00
return ;
2022-06-03 21:37:35 +10:00
}
2022-06-04 19:09:04 +10:00
_chatSystem . DispatchStationAnnouncement ( uid , msg , title , colorOverride : comp . AnnouncementColor ) ;
2022-08-07 20:21:56 -03:00
if ( message . Session . AttachedEntity ! = null )
_adminLogger . Add ( LogType . Chat , LogImpact . Low , $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following station announcement: {msg}" ) ;
2022-06-03 21:37:35 +10:00
}
private void OnCallShuttleMessage ( EntityUid uid , CommunicationsConsoleComponent comp , CommunicationsConsoleCallEmergencyShuttleMessage message )
{
2022-07-01 13:40:36 -07:00
if ( ! CanCallOrRecall ( comp ) ) return ;
2023-07-08 09:02:17 -07:00
if ( message . Session . AttachedEntity is not { Valid : true } mob ) return ;
2022-06-03 21:37:35 +10:00
if ( ! CanUse ( mob , uid ) )
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( "comms-console-permission-denied" ) , uid , message . Session ) ;
2022-06-03 21:37:35 +10:00
return ;
}
_roundEndSystem . RequestRoundEnd ( uid ) ;
2022-08-07 20:21:56 -03:00
_adminLogger . Add ( LogType . Action , LogImpact . Extreme , $"{ToPrettyString(mob):player} has called the shuttle." ) ;
2022-06-03 21:37:35 +10:00
}
private void OnRecallShuttleMessage ( EntityUid uid , CommunicationsConsoleComponent comp , CommunicationsConsoleRecallEmergencyShuttleMessage message )
{
2022-07-01 13:40:36 -07:00
if ( ! CanCallOrRecall ( comp ) ) return ;
2023-07-08 09:02:17 -07:00
if ( message . Session . AttachedEntity is not { Valid : true } mob ) return ;
2022-06-03 21:37:35 +10:00
if ( ! CanUse ( mob , uid ) )
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( "comms-console-permission-denied" ) , uid , message . Session ) ;
2022-06-03 21:37:35 +10:00
return ;
}
2022-07-01 13:40:36 -07:00
2022-06-03 21:37:35 +10:00
_roundEndSystem . CancelRoundEndCountdown ( uid ) ;
2022-08-07 20:21:56 -03:00
_adminLogger . Add ( LogType . Action , LogImpact . Extreme , $"{ToPrettyString(mob):player} has recalled the shuttle." ) ;
2022-06-03 21:37:35 +10:00
}
}
}