2022-12-19 21:38:34 -05:00
using Content.Server.Administration.Logs ;
using Content.Server.Atmos.EntitySystems ;
2023-04-25 08:19:14 -05:00
using Content.Server.Chat.Managers ;
2022-12-19 21:38:34 -05:00
using Content.Server.GameTicking ;
using Content.Server.Station.Components ;
using Content.Server.Station.Systems ;
using Content.Shared.Database ;
using Content.Shared.Maps ;
2023-07-06 16:43:49 +12:00
using Content.Shared.Physics ;
2022-12-19 21:38:34 -05:00
using Content.Shared.Respawn ;
using Robust.Shared.Map ;
2024-03-22 03:08:40 -04:00
using Robust.Shared.Map.Components ;
2022-12-19 21:38:34 -05:00
using Robust.Shared.Random ;
namespace Content.Server.Respawn ;
public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem
{
[Dependency] private readonly IAdminLogManager _adminLog = default ! ;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default ! ;
[Dependency] private readonly AtmosphereSystem _atmosphere = default ! ;
[Dependency] private readonly IRobustRandom _random = default ! ;
2024-03-20 21:59:56 -04:00
[Dependency] private readonly SharedTransformSystem _transform = default ! ;
2023-07-06 16:43:49 +12:00
[Dependency] private readonly TurfSystem _turf = default ! ;
2023-04-25 08:19:14 -05:00
[Dependency] private readonly IChatManager _chat = default ! ;
2022-12-19 21:38:34 -05:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < GameRunLevelChangedEvent > ( OnRunLevelChanged ) ;
SubscribeLocalEvent < SpecialRespawnSetupEvent > ( OnSpecialRespawnSetup ) ;
SubscribeLocalEvent < SpecialRespawnComponent , ComponentStartup > ( OnStartup ) ;
SubscribeLocalEvent < SpecialRespawnComponent , EntityTerminatingEvent > ( OnTermination ) ;
}
private void OnRunLevelChanged ( GameRunLevelChangedEvent ev )
{
//Try to compensate for restartroundnow command
if ( ev . Old = = GameRunLevel . InRound & & ev . New = = GameRunLevel . PreRoundLobby )
OnRoundEnd ( ) ;
switch ( ev . New )
{
case GameRunLevel . PostRound :
OnRoundEnd ( ) ;
break ;
}
}
private void OnSpecialRespawnSetup ( SpecialRespawnSetupEvent ev )
{
if ( ! TryComp < SpecialRespawnComponent > ( ev . Entity , out var comp ) )
return ;
var xform = Transform ( ev . Entity ) ;
if ( xform . GridUid ! = null )
comp . StationMap = ( xform . MapUid , xform . GridUid ) ;
}
private void OnRoundEnd ( )
{
var specialRespawnQuery = EntityQuery < SpecialRespawnComponent > ( ) ;
//Turn respawning off so the entity doesn't respawn during reset
foreach ( var entity in specialRespawnQuery )
{
entity . Respawn = false ;
}
}
private void OnStartup ( EntityUid uid , SpecialRespawnComponent component , ComponentStartup args )
{
var ev = new SpecialRespawnSetupEvent ( uid ) ;
QueueLocalEvent ( ev ) ;
}
private void OnTermination ( EntityUid uid , SpecialRespawnComponent component , ref EntityTerminatingEvent args )
{
var entityMapUid = component . StationMap . Item1 ;
var entityGridUid = component . StationMap . Item2 ;
if ( ! component . Respawn | | ! HasComp < StationMemberComponent > ( entityGridUid ) | | entityMapUid = = null )
return ;
2024-03-22 03:08:40 -04:00
if ( ! TryComp < MapGridComponent > ( entityGridUid , out var grid ) | | MetaData ( entityGridUid . Value ) . EntityLifeStage > = EntityLifeStage . Terminating )
2022-12-19 21:38:34 -05:00
return ;
if ( TryFindRandomTile ( entityGridUid . Value , entityMapUid . Value , 10 , out var coords ) )
2023-04-25 08:19:14 -05:00
Respawn ( uid , component . Prototype , coords ) ;
2022-12-19 21:38:34 -05:00
//If the above fails, spawn at the center of the grid on the station
else
{
var xform = Transform ( entityGridUid . Value ) ;
var pos = xform . Coordinates ;
2024-02-28 00:51:20 +11:00
var mapPos = xform . MapPosition ;
2022-12-19 21:38:34 -05:00
var circle = new Circle ( mapPos . Position , 2 ) ;
var found = false ;
foreach ( var tile in grid . GetTilesIntersecting ( circle ) )
{
2023-07-06 16:43:49 +12:00
if ( tile . IsSpace ( _tileDefinitionManager )
| | _turf . IsTileBlocked ( tile , CollisionGroup . MobMask )
| | ! _atmosphere . IsTileMixtureProbablySafe ( entityGridUid , entityMapUid . Value ,
grid . TileIndicesFor ( mapPos ) ) )
{
2022-12-19 21:38:34 -05:00
continue ;
2023-07-06 16:43:49 +12:00
}
2022-12-19 21:38:34 -05:00
2023-07-06 16:43:49 +12:00
pos = _turf . GetTileCenter ( tile ) ;
2022-12-19 21:38:34 -05:00
found = true ;
if ( found )
break ;
}
2023-04-25 08:19:14 -05:00
Respawn ( uid , component . Prototype , pos ) ;
2022-12-19 21:38:34 -05:00
}
}
/// <summary>
/// Respawn the entity and log it.
/// </summary>
2023-04-25 08:19:14 -05:00
/// <param name="oldEntity">The entity being deleted</param>
2022-12-19 21:38:34 -05:00
/// <param name="prototype">The prototype being spawned</param>
/// <param name="coords">The place where it will be spawned</param>
2023-04-25 08:19:14 -05:00
private void Respawn ( EntityUid oldEntity , string prototype , EntityCoordinates coords )
2022-12-19 21:38:34 -05:00
{
var entity = Spawn ( prototype , coords ) ;
2024-03-20 21:59:56 -04:00
_adminLog . Add ( LogType . Respawn , LogImpact . High , $"{ToPrettyString(oldEntity)} was deleted and was respawned at {coords.ToMap(EntityManager, _transform)} as {ToPrettyString(entity)}" ) ;
2023-04-25 08:19:14 -05:00
_chat . SendAdminAlert ( $"{MetaData(oldEntity).EntityName} was deleted and was respawned as {ToPrettyString(entity)}" ) ;
2022-12-19 21:38:34 -05:00
}
2024-03-20 21:59:56 -04:00
/// <summary>
2022-12-19 21:38:34 -05:00
/// Try to find a random safe tile on the supplied grid
/// </summary>
/// <param name="targetGrid">The grid that you're looking for a safe tile on</param>
/// <param name="targetMap">The map that you're looking for a safe tile on</param>
/// <param name="maxAttempts">The maximum amount of attempts it should try before it gives up</param>
/// <param name="targetCoords">If successful, the coordinates of the safe tile</param>
/// <returns></returns>
public bool TryFindRandomTile ( EntityUid targetGrid , EntityUid targetMap , int maxAttempts , out EntityCoordinates targetCoords )
{
targetCoords = EntityCoordinates . Invalid ;
2024-03-22 03:08:40 -04:00
if ( ! TryComp < MapGridComponent > ( targetGrid , out var grid ) )
2022-12-19 21:38:34 -05:00
return false ;
var xform = Transform ( targetGrid ) ;
if ( ! grid . TryGetTileRef ( xform . Coordinates , out var tileRef ) )
return false ;
var tile = tileRef . GridIndices ;
var found = false ;
2024-02-28 00:51:20 +11:00
var ( gridPos , _ , gridMatrix ) = xform . GetWorldPositionRotationMatrix ( ) ;
2022-12-19 21:38:34 -05:00
var gridBounds = gridMatrix . TransformBox ( grid . LocalAABB ) ;
//Obviously don't put anything ridiculous in here
for ( var i = 0 ; i < maxAttempts ; i + + )
{
var randomX = _random . Next ( ( int ) gridBounds . Left , ( int ) gridBounds . Right ) ;
var randomY = _random . Next ( ( int ) gridBounds . Bottom , ( int ) gridBounds . Top ) ;
tile = new Vector2i ( randomX - ( int ) gridPos . X , randomY - ( int ) gridPos . Y ) ;
var mapPos = grid . GridTileToWorldPos ( tile ) ;
var mapTarget = grid . WorldToTile ( mapPos ) ;
var circle = new Circle ( mapPos , 2 ) ;
foreach ( var newTileRef in grid . GetTilesIntersecting ( circle ) )
{
if ( newTileRef . IsSpace ( _tileDefinitionManager ) | | newTileRef . IsBlockedTurf ( true ) | | ! _atmosphere . IsTileMixtureProbablySafe ( targetGrid , targetMap , mapTarget ) )
continue ;
found = true ;
targetCoords = grid . GridTileToLocal ( tile ) ;
break ;
}
//Found a safe tile, no need to continue
if ( found )
break ;
}
if ( ! found )
return false ;
return true ;
}
}