2021-02-12 04:35:56 +01:00
using System.Collections.Generic ;
using Content.Server.Administration ;
2022-01-09 17:54:36 -08:00
using Content.Server.Administration.Logs ;
2021-06-09 22:19:39 +02:00
using Content.Server.EUI ;
using Content.Server.Ghost.Components ;
using Content.Server.Ghost.Roles.Components ;
using Content.Server.Ghost.Roles.UI ;
2022-01-01 08:48:45 -08:00
using Content.Server.Mind.Components ;
2022-01-01 22:00:21 -08:00
using Content.Server.MobState.States ;
2021-11-15 18:14:34 +00:00
using Content.Server.Players ;
2021-12-13 16:45:49 +11:00
using Content.Shared.Administration ;
2022-01-09 17:54:36 -08:00
using Content.Shared.Database ;
2021-02-12 04:35:56 +01:00
using Content.Shared.GameTicking ;
2021-11-03 23:48:12 +00:00
using Content.Shared.Ghost ;
2021-12-05 21:02:04 +01:00
using Content.Shared.Ghost.Roles ;
2022-01-01 22:00:21 -08:00
using Content.Shared.MobState ;
2021-02-12 04:35:56 +01:00
using JetBrains.Annotations ;
using Robust.Server.GameObjects ;
2021-02-12 10:45:22 +01:00
using Robust.Server.Player ;
2021-02-12 04:35:56 +01:00
using Robust.Shared.Console ;
2021-12-05 21:02:04 +01:00
using Robust.Shared.Enums ;
2021-02-12 10:45:22 +01:00
using Robust.Shared.GameObjects ;
2021-02-12 04:35:56 +01:00
using Robust.Shared.IoC ;
2021-11-15 18:14:34 +00:00
using Robust.Shared.Utility ;
2021-12-05 21:02:04 +01:00
using Robust.Shared.ViewVariables ;
2021-02-12 04:35:56 +01:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Ghost.Roles
2021-02-12 04:35:56 +01:00
{
[UsedImplicitly]
2022-02-16 00:23:23 -07:00
public sealed class GhostRoleSystem : EntitySystem
2021-02-12 04:35:56 +01:00
{
[Dependency] private readonly EuiManager _euiManager = default ! ;
2021-11-22 23:11:48 -08:00
[Dependency] private readonly IPlayerManager _playerManager = default ! ;
2022-01-09 17:54:36 -08:00
[Dependency] private readonly AdminLogSystem _adminLogSystem = default ! ;
2021-02-12 04:35:56 +01:00
2021-11-22 23:11:48 -08:00
private uint _nextRoleIdentifier ;
2021-11-03 23:48:12 +00:00
private bool _needsUpdateGhostRoleCount = true ;
2021-02-12 04:35:56 +01:00
private readonly Dictionary < uint , GhostRoleComponent > _ghostRoles = new ( ) ;
private readonly Dictionary < IPlayerSession , GhostRolesEui > _openUis = new ( ) ;
2021-02-16 09:51:27 +01:00
private readonly Dictionary < IPlayerSession , MakeGhostRoleEui > _openMakeGhostRoleUis = new ( ) ;
2021-02-12 04:35:56 +01:00
[ViewVariables]
public IReadOnlyCollection < GhostRoleComponent > GhostRoles = > _ghostRoles . Values ;
public override void Initialize ( )
{
2021-04-09 16:08:12 +02:00
base . Initialize ( ) ;
2021-06-29 15:56:07 +02:00
SubscribeLocalEvent < RoundRestartCleanupEvent > ( Reset ) ;
2021-05-30 11:39:42 +02:00
SubscribeLocalEvent < PlayerAttachedEvent > ( OnPlayerAttached ) ;
2022-01-12 18:25:05 +11:00
SubscribeLocalEvent < GhostTakeoverAvailableComponent , MindAddedMessage > ( OnMindAdded ) ;
2022-01-01 22:00:21 -08:00
SubscribeLocalEvent < GhostTakeoverAvailableComponent , MindRemovedMessage > ( OnMindRemoved ) ;
SubscribeLocalEvent < GhostTakeoverAvailableComponent , MobStateChangedEvent > ( OnMobStateChanged ) ;
2021-11-03 23:48:12 +00:00
_playerManager . PlayerStatusChanged + = PlayerStatusChanged ;
}
2022-01-01 22:00:21 -08:00
private void OnMobStateChanged ( EntityUid uid , GhostRoleComponent component , MobStateChangedEvent args )
{
switch ( args . CurrentMobState )
{
case NormalMobState :
{
if ( ! component . Taken )
RegisterGhostRole ( component ) ;
break ;
}
case CriticalMobState :
case DeadMobState :
UnregisterGhostRole ( component ) ;
break ;
}
}
2021-11-03 23:48:12 +00:00
public override void Shutdown ( )
{
base . Shutdown ( ) ;
_playerManager . PlayerStatusChanged - = PlayerStatusChanged ;
2021-02-12 04:35:56 +01:00
}
private uint GetNextRoleIdentifier ( )
{
return unchecked ( _nextRoleIdentifier + + ) ;
}
public void OpenEui ( IPlayerSession session )
{
2021-12-05 21:02:04 +01:00
if ( session . AttachedEntity is not { Valid : true } attached | |
! EntityManager . HasComponent < GhostComponent > ( attached ) )
2021-02-12 04:35:56 +01:00
return ;
if ( _openUis . ContainsKey ( session ) )
CloseEui ( session ) ;
var eui = _openUis [ session ] = new GhostRolesEui ( ) ;
_euiManager . OpenEui ( eui , session ) ;
eui . StateDirty ( ) ;
}
2021-02-16 09:51:27 +01:00
public void OpenMakeGhostRoleEui ( IPlayerSession session , EntityUid uid )
{
if ( session . AttachedEntity = = null )
return ;
if ( _openMakeGhostRoleUis . ContainsKey ( session ) )
CloseEui ( session ) ;
var eui = _openMakeGhostRoleUis [ session ] = new MakeGhostRoleEui ( uid ) ;
_euiManager . OpenEui ( eui , session ) ;
eui . StateDirty ( ) ;
}
2021-02-12 04:35:56 +01:00
public void CloseEui ( IPlayerSession session )
{
if ( ! _openUis . ContainsKey ( session ) ) return ;
_openUis . Remove ( session , out var eui ) ;
eui ? . Close ( ) ;
}
2021-02-16 09:51:27 +01:00
public void CloseMakeGhostRoleEui ( IPlayerSession session )
{
if ( _openMakeGhostRoleUis . Remove ( session , out var eui ) )
{
2021-11-22 23:11:48 -08:00
eui . Close ( ) ;
2021-02-16 09:51:27 +01:00
}
}
2021-02-12 04:35:56 +01:00
public void UpdateAllEui ( )
{
foreach ( var eui in _openUis . Values )
{
eui . StateDirty ( ) ;
}
2021-11-03 23:48:12 +00:00
// Note that this, like the EUIs, is deferred.
// This is for roughly the same reasons, too:
// Someone might spawn a ton of ghost roles at once.
_needsUpdateGhostRoleCount = true ;
}
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
if ( _needsUpdateGhostRoleCount )
{
_needsUpdateGhostRoleCount = false ;
var response = new GhostUpdateGhostRoleCountEvent ( _ghostRoles . Count ) ;
2021-11-22 23:11:48 -08:00
foreach ( var player in _playerManager . Sessions )
{
2021-11-03 23:48:12 +00:00
RaiseNetworkEvent ( response , player . ConnectedClient ) ;
2021-11-22 23:11:48 -08:00
}
2021-11-03 23:48:12 +00:00
}
}
private void PlayerStatusChanged ( object? blah , SessionStatusEventArgs args )
{
if ( args . NewStatus = = SessionStatus . InGame )
{
var response = new GhostUpdateGhostRoleCountEvent ( _ghostRoles . Count ) ;
RaiseNetworkEvent ( response , args . Session . ConnectedClient ) ;
}
2021-02-12 04:35:56 +01:00
}
public void RegisterGhostRole ( GhostRoleComponent role )
{
if ( _ghostRoles . ContainsValue ( role ) ) return ;
_ghostRoles [ role . Identifier = GetNextRoleIdentifier ( ) ] = role ;
UpdateAllEui ( ) ;
}
public void UnregisterGhostRole ( GhostRoleComponent role )
{
if ( ! _ghostRoles . ContainsKey ( role . Identifier ) | | _ghostRoles [ role . Identifier ] ! = role ) return ;
_ghostRoles . Remove ( role . Identifier ) ;
UpdateAllEui ( ) ;
}
public void Takeover ( IPlayerSession player , uint identifier )
{
if ( ! _ghostRoles . TryGetValue ( identifier , out var role ) ) return ;
if ( ! role . Take ( player ) ) return ;
2022-01-09 17:54:36 -08:00
if ( player . AttachedEntity ! = null )
_adminLogSystem . Add ( LogType . GhostRoleTaken , LogImpact . Low , $"{player:player} took the {role.RoleName:roleName} ghost role {ToPrettyString(player.AttachedEntity.Value):entity}" ) ;
2021-02-12 04:35:56 +01:00
CloseEui ( player ) ;
}
2021-11-15 18:14:34 +00:00
public void GhostRoleInternalCreateMindAndTransfer ( IPlayerSession player , EntityUid roleUid , EntityUid mob , GhostRoleComponent ? role = null )
{
if ( ! Resolve ( roleUid , ref role ) ) return ;
var contentData = player . ContentData ( ) ;
DebugTools . AssertNotNull ( contentData ) ;
var newMind = new Mind . Mind ( player . UserId )
{
CharacterName = EntityManager . GetComponent < MetaDataComponent > ( mob ) . EntityName
} ;
newMind . AddRole ( new GhostRoleMarkerRole ( newMind , role . RoleName ) ) ;
newMind . ChangeOwningPlayer ( player . UserId ) ;
newMind . TransferTo ( mob ) ;
}
2021-02-12 04:35:56 +01:00
public GhostRoleInfo [ ] GetGhostRolesInfo ( )
{
var roles = new GhostRoleInfo [ _ghostRoles . Count ] ;
var i = 0 ;
foreach ( var ( id , role ) in _ghostRoles )
{
2021-11-02 00:42:04 +00:00
roles [ i ] = new GhostRoleInfo ( ) { Identifier = id , Name = role . RoleName , Description = role . RoleDescription , Rules = role . RoleRules } ;
2021-02-12 04:35:56 +01:00
i + + ;
}
return roles ;
}
2021-05-30 11:39:42 +02:00
private void OnPlayerAttached ( PlayerAttachedEvent message )
2021-02-12 04:35:56 +01:00
{
// Close the session of any player that has a ghost roles window open and isn't a ghost anymore.
2021-05-30 11:39:42 +02:00
if ( ! _openUis . ContainsKey ( message . Player ) ) return ;
2021-12-05 21:02:04 +01:00
if ( EntityManager . HasComponent < GhostComponent > ( message . Entity ) ) return ;
2021-05-30 11:39:42 +02:00
CloseEui ( message . Player ) ;
2021-02-12 04:35:56 +01:00
}
2022-01-12 18:25:05 +11:00
private void OnMindAdded ( EntityUid uid , GhostTakeoverAvailableComponent component , MindAddedMessage args )
{
component . Taken = true ;
UnregisterGhostRole ( component ) ;
}
2022-01-01 08:48:45 -08:00
private void OnMindRemoved ( EntityUid uid , GhostRoleComponent component , MindRemovedMessage args )
{
2022-01-11 14:12:19 +11:00
// Avoid re-registering it for duplicate entries and potential exceptions.
if ( ! component . ReregisterOnGhost | | component . LifeStage > ComponentLifeStage . Running )
2022-01-01 08:48:45 -08:00
return ;
2022-01-11 14:12:19 +11:00
2022-01-01 08:48:45 -08:00
component . Taken = false ;
RegisterGhostRole ( component ) ;
}
2021-06-29 15:56:07 +02:00
public void Reset ( RoundRestartCleanupEvent ev )
2021-02-12 04:35:56 +01:00
{
foreach ( var session in _openUis . Keys )
{
CloseEui ( session ) ;
}
_openUis . Clear ( ) ;
_ghostRoles . Clear ( ) ;
_nextRoleIdentifier = 0 ;
}
}
[AnyCommand]
2022-02-16 00:23:23 -07:00
public sealed class GhostRoles : IConsoleCommand
2021-02-12 04:35:56 +01:00
{
public string Command = > "ghostroles" ;
public string Description = > "Opens the ghost role request window." ;
public string Help = > $"{Command}" ;
public void Execute ( IConsoleShell shell , string argStr , string [ ] args )
{
if ( shell . Player ! = null )
EntitySystem . Get < GhostRoleSystem > ( ) . OpenEui ( ( IPlayerSession ) shell . Player ) ;
else
shell . WriteLine ( "You can only open the ghost roles UI on a client." ) ;
}
}
}