2022-01-04 13:37:50 +01:00
using Content.Server.GameTicking ;
using Content.Server.Ghost ;
using Content.Server.Ghost.Components ;
using Content.Server.Mind.Components ;
using Content.Shared.Examine ;
2023-01-13 16:57:10 -08:00
using Content.Shared.Mobs.Components ;
using Content.Shared.Mobs.Systems ;
2022-01-04 13:37:50 +01:00
using Robust.Shared.Map ;
using Robust.Shared.Timing ;
namespace Content.Server.Mind ;
2022-02-16 00:23:23 -07:00
public sealed class MindSystem : EntitySystem
2022-01-04 13:37:50 +01:00
{
[Dependency] private readonly IMapManager _mapManager = default ! ;
[Dependency] private readonly GameTicker _gameTicker = default ! ;
2022-12-19 19:25:35 -08:00
[Dependency] private readonly MobStateSystem _mobStateSystem = default ! ;
2022-01-04 13:37:50 +01:00
[Dependency] private readonly GhostSystem _ghostSystem = default ! ;
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < MindComponent , ComponentShutdown > ( OnShutdown ) ;
SubscribeLocalEvent < MindComponent , ExaminedEvent > ( OnExamined ) ;
}
public void SetGhostOnShutdown ( EntityUid uid , bool value , MindComponent ? mind = null )
{
if ( ! Resolve ( uid , ref mind ) )
return ;
mind . GhostOnShutdown = value ;
}
/// <summary>
/// Don't call this unless you know what the hell you're doing.
/// Use <see cref="Mind.TransferTo(System.Nullable{Robust.Shared.GameObjects.EntityUid},bool)"/> instead.
/// If that doesn't cover it, make something to cover it.
/// </summary>
public void InternalAssignMind ( EntityUid uid , Mind value , MindComponent ? mind = null )
{
if ( ! Resolve ( uid , ref mind ) )
return ;
mind . Mind = value ;
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( uid , new MindAddedMessage ( ) , true ) ;
2022-01-04 13:37:50 +01:00
}
/// <summary>
/// Don't call this unless you know what the hell you're doing.
/// Use <see cref="Mind.TransferTo(System.Nullable{Robust.Shared.GameObjects.EntityUid},bool)"/> instead.
/// If that doesn't cover it, make something to cover it.
/// </summary>
public void InternalEjectMind ( EntityUid uid , MindComponent ? mind = null )
{
if ( ! Resolve ( uid , ref mind ) )
return ;
if ( ! Deleted ( uid ) )
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( uid , new MindRemovedMessage ( ) , true ) ;
2022-01-04 13:37:50 +01:00
mind . Mind = null ;
}
private void OnShutdown ( EntityUid uid , MindComponent mind , ComponentShutdown args )
{
// Let's not create ghosts if not in the middle of the round.
if ( _gameTicker . RunLevel ! = GameRunLevel . InRound )
return ;
if ( mind . HasMind )
{
if ( mind . Mind ? . VisitingEntity is { Valid : true } visiting )
{
if ( TryComp ( visiting , out GhostComponent ? ghost ) )
{
_ghostSystem . SetCanReturnToBody ( ghost , false ) ;
}
mind . Mind ! . TransferTo ( visiting ) ;
}
else if ( mind . GhostOnShutdown )
{
2022-08-26 01:33:40 +12:00
// Changing an entities parents while deleting is VERY sus. This WILL throw exceptions.
// TODO: just find the applicable spawn position dirctly without actually updating the transform's parent.
2022-06-01 03:39:50 -04:00
Transform ( uid ) . AttachToGridOrMap ( ) ;
2022-01-04 13:37:50 +01:00
var spawnPosition = Transform ( uid ) . Coordinates ;
2022-08-26 01:33:40 +12:00
2022-01-04 13:37:50 +01:00
// Use a regular timer here because the entity has probably been deleted.
Timer . Spawn ( 0 , ( ) = >
{
2022-03-07 12:56:19 +01:00
// Make extra sure the round didn't end between spawning the timer and it being executed.
if ( _gameTicker . RunLevel ! = GameRunLevel . InRound )
return ;
2022-01-04 13:37:50 +01:00
// Async this so that we don't throw if the grid we're on is being deleted.
2022-06-20 12:14:35 +12:00
var gridId = spawnPosition . GetGridUid ( EntityManager ) ;
2022-06-11 18:54:41 -07:00
if ( ! spawnPosition . IsValid ( EntityManager ) | | gridId = = EntityUid . Invalid | | ! _mapManager . GridExists ( gridId ) )
2022-01-04 13:37:50 +01:00
{
2022-03-07 12:56:19 +01:00
spawnPosition = _gameTicker . GetObserverSpawnPoint ( ) ;
2022-01-04 13:37:50 +01:00
}
2022-08-26 01:33:40 +12:00
// TODO refactor observer spawning.
2023-01-24 13:33:49 +13:00
// please.
2022-08-26 01:33:40 +12:00
if ( ! spawnPosition . IsValid ( EntityManager ) )
{
2023-01-24 13:33:49 +13:00
// This should be an error, if it didn't cause tests to start erroring when they delete a player.
Logger . WarningS ( "mind" , $"Entity \" { ToPrettyString ( uid ) } \ " for {mind.Mind?.CharacterName} was deleted, and no applicable spawn location is available." ) ;
2023-02-14 15:46:44 +13:00
mind . Mind ? . TransferTo ( null ) ;
2022-08-26 01:33:40 +12:00
return ;
}
2022-01-04 13:37:50 +01:00
var ghost = Spawn ( "MobObserver" , spawnPosition ) ;
var ghostComponent = Comp < GhostComponent > ( ghost ) ;
_ghostSystem . SetCanReturnToBody ( ghostComponent , false ) ;
2022-03-07 12:56:19 +01:00
// Log these to make sure they're not causing the GameTicker round restart bugs...
Logger . DebugS ( "mind" , $"Entity \" { ToPrettyString ( uid ) } \ " for {mind.Mind?.CharacterName} was deleted, spawned \"{ToPrettyString(ghost)}\"." ) ;
2022-01-04 13:37:50 +01:00
if ( mind . Mind = = null )
return ;
var val = mind . Mind . CharacterName ? ? string . Empty ;
MetaData ( ghost ) . EntityName = val ;
mind . Mind . TransferTo ( ghost ) ;
} ) ;
}
}
}
private void OnExamined ( EntityUid uid , MindComponent mind , ExaminedEvent args )
{
if ( ! mind . ShowExamineInfo | | ! args . IsInDetailsRange )
{
return ;
}
2022-12-19 19:25:35 -08:00
var dead = TryComp < MobStateComponent ? > ( uid , out var state ) & & _mobStateSystem . IsDead ( uid , state ) ;
2022-01-04 13:37:50 +01:00
if ( dead )
{
if ( mind . Mind ? . Session = = null ) {
// Player has no session attached and dead
args . PushMarkup ( $"[color=yellow]{Loc.GetString(" mind - component - no - mind - and - dead - text ", (" ent ", uid))}[/color]" ) ;
} else {
// Player is dead with session
args . PushMarkup ( $"[color=red]{Loc.GetString(" comp - mind - examined - dead ", (" ent ", uid))}[/color]" ) ;
}
}
else if ( ! mind . HasMind )
{
args . PushMarkup ( $"[color=mediumpurple]{Loc.GetString(" comp - mind - examined - catatonic ", (" ent ", uid))}[/color]" ) ;
}
else if ( mind . Mind ? . Session = = null )
{
args . PushMarkup ( $"[color=yellow]{Loc.GetString(" comp - mind - examined - ssd ", (" ent ", uid))}[/color]" ) ;
}
}
}