2022-03-25 22:10:43 +01:00
using Content.Server.Administration.Logs ;
2021-11-09 08:08:30 +01:00
using Content.Server.Coordinates.Helpers ;
2022-06-06 09:04:40 +10:00
using Content.Server.Popups ;
2021-11-09 08:08:30 +01:00
using Content.Server.Pulling ;
2022-04-02 16:52:44 +13:00
using Content.Shared.Construction.Components ;
using Content.Shared.Construction.EntitySystems ;
2022-03-25 22:10:43 +01:00
using Content.Shared.Database ;
2022-05-30 04:55:54 -04:00
using Content.Shared.Examine ;
2021-11-09 08:08:30 +01:00
using Content.Shared.Pulling.Components ;
2023-02-24 19:01:25 -05:00
using Content.Shared.Tools ;
2022-04-02 16:52:44 +13:00
using Content.Shared.Tools.Components ;
2022-08-11 12:11:29 +10:00
using Robust.Shared.Map ;
2023-03-10 16:41:22 +11:00
using Robust.Shared.Map.Components ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Components ;
2021-11-09 08:08:30 +01:00
namespace Content.Server.Construction
{
2022-04-02 16:52:44 +13:00
public sealed class AnchorableSystem : SharedAnchorableSystem
2021-11-09 08:08:30 +01:00
{
2022-05-28 23:41:17 -07:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2022-08-11 12:11:29 +10:00
[Dependency] private readonly IMapManager _mapManager = default ! ;
2022-06-06 09:04:40 +10:00
[Dependency] private readonly PopupSystem _popup = default ! ;
2023-02-24 19:01:25 -05:00
[Dependency] private readonly SharedToolSystem _tool = default ! ;
2022-09-11 06:30:10 +00:00
[Dependency] private readonly PullingSystem _pulling = default ! ;
2021-11-09 08:08:30 +01:00
2022-05-18 12:35:44 +10:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-06-06 09:04:40 +10:00
SubscribeLocalEvent < AnchorableComponent , TryAnchorCompletedEvent > ( OnAnchorComplete ) ;
SubscribeLocalEvent < AnchorableComponent , TryUnanchorCompletedEvent > ( OnUnanchorComplete ) ;
2022-05-30 04:55:54 -04:00
SubscribeLocalEvent < AnchorableComponent , ExaminedEvent > ( OnAnchoredExamine ) ;
}
private void OnAnchoredExamine ( EntityUid uid , AnchorableComponent component , ExaminedEvent args )
{
var isAnchored = Comp < TransformComponent > ( uid ) . Anchored ;
var messageId = isAnchored ? "examinable-anchored" : "examinable-unanchored" ;
args . PushMarkup ( Loc . GetString ( messageId , ( "target" , uid ) ) ) ;
2022-05-18 12:35:44 +10:00
}
2022-06-06 09:04:40 +10:00
private void OnUnanchorComplete ( EntityUid uid , AnchorableComponent component , TryUnanchorCompletedEvent args )
2022-05-18 12:35:44 +10:00
{
var xform = Transform ( uid ) ;
2022-10-30 02:48:53 -04:00
RaiseLocalEvent ( uid , new BeforeUnanchoredEvent ( args . User , args . Using ) ) ;
2022-05-18 12:35:44 +10:00
xform . Anchored = false ;
2022-10-30 02:48:53 -04:00
RaiseLocalEvent ( uid , new UserUnanchoredEvent ( args . User , args . Using ) ) ;
2022-05-18 12:35:44 +10:00
2022-12-19 10:41:47 +13:00
_popup . PopupEntity ( Loc . GetString ( "anchorable-unanchored" ) , uid ) ;
2022-06-06 09:04:40 +10:00
2022-05-28 23:41:17 -07:00
_adminLogger . Add (
2022-12-08 19:17:56 -06:00
LogType . Unanchor ,
2022-05-18 12:35:44 +10:00
LogImpact . Low ,
$"{EntityManager.ToPrettyString(args.User):user} unanchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(args.Using):using}"
) ;
}
2022-06-06 09:04:40 +10:00
private void OnAnchorComplete ( EntityUid uid , AnchorableComponent component , TryAnchorCompletedEvent args )
2022-05-18 12:35:44 +10:00
{
var xform = Transform ( uid ) ;
2022-08-11 12:11:29 +10:00
if ( TryComp < PhysicsComponent > ( uid , out var anchorBody ) & &
! TileFree ( xform . Coordinates , anchorBody ) )
{
2022-12-19 10:41:47 +13:00
_popup . PopupEntity ( Loc . GetString ( "anchorable-occupied" ) , uid , args . User ) ;
2022-08-11 12:11:29 +10:00
return ;
}
2022-05-18 12:35:44 +10:00
// Snap rotation to cardinal (multiple of 90)
var rot = xform . LocalRotation ;
xform . LocalRotation = Math . Round ( rot / ( Math . PI / 2 ) ) * ( Math . PI / 2 ) ;
if ( TryComp < SharedPullableComponent > ( uid , out var pullable ) & & pullable . Puller ! = null )
{
2022-09-11 06:30:10 +00:00
_pulling . TryStopPull ( pullable ) ;
2022-05-18 12:35:44 +10:00
}
2022-08-11 12:11:29 +10:00
// TODO: Anchoring snaps rn anyway!
2022-05-18 12:35:44 +10:00
if ( component . Snap )
2022-08-11 12:11:29 +10:00
xform . Coordinates = xform . Coordinates . SnapToGrid ( EntityManager , _mapManager ) ;
2022-05-18 12:35:44 +10:00
2022-10-30 02:48:53 -04:00
RaiseLocalEvent ( uid , new BeforeAnchoredEvent ( args . User , args . Using ) ) ;
2022-05-18 12:35:44 +10:00
xform . Anchored = true ;
2022-10-30 02:48:53 -04:00
RaiseLocalEvent ( uid , new UserAnchoredEvent ( args . User , args . Using ) ) ;
2022-05-18 12:35:44 +10:00
2022-12-19 10:41:47 +13:00
_popup . PopupEntity ( Loc . GetString ( "anchorable-anchored" ) , uid ) ;
2022-06-06 09:04:40 +10:00
2022-05-28 23:41:17 -07:00
_adminLogger . Add (
2022-12-08 19:17:56 -06:00
LogType . Anchor ,
2022-05-18 12:35:44 +10:00
LogImpact . Low ,
$"{EntityManager.ToPrettyString(args.User):user} anchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(args.Using):using}"
) ;
}
2022-08-11 12:11:29 +10:00
private bool TileFree ( EntityCoordinates coordinates , PhysicsComponent anchorBody )
{
// Probably ignore CanCollide on the anchoring body?
var gridUid = coordinates . GetGridUid ( EntityManager ) ;
if ( ! _mapManager . TryGetGrid ( gridUid , out var grid ) )
return false ;
var tileIndices = grid . TileIndicesFor ( coordinates ) ;
2023-03-10 16:41:22 +11:00
return TileFree ( grid , tileIndices , anchorBody . CollisionLayer , anchorBody . CollisionMask ) ;
}
public bool TileFree ( MapGridComponent grid , Vector2i gridIndices , int collisionLayer = 0 , int collisionMask = 0 )
{
var enumerator = grid . GetAnchoredEntitiesEnumerator ( gridIndices ) ;
2022-08-11 12:11:29 +10:00
var bodyQuery = GetEntityQuery < PhysicsComponent > ( ) ;
while ( enumerator . MoveNext ( out var ent ) )
{
if ( ! bodyQuery . TryGetComponent ( ent , out var body ) | |
2022-09-11 19:19:27 +10:00
! body . CanCollide | |
2023-01-11 02:16:16 -06:00
! body . Hard )
2022-09-11 19:19:27 +10:00
{
2022-08-11 12:11:29 +10:00
continue ;
2022-09-11 19:19:27 +10:00
}
2022-08-11 12:11:29 +10:00
2023-03-10 16:41:22 +11:00
if ( ( body . CollisionMask & collisionLayer ) ! = 0x0 | |
( body . CollisionLayer & collisionMask ) ! = 0x0 )
2022-08-11 12:11:29 +10:00
{
return false ;
}
}
return true ;
}
2021-11-09 08:08:30 +01:00
/// <summary>
/// Checks if a tool can change the anchored status.
/// </summary>
/// <returns>true if it is valid, false otherwise</returns>
2022-05-18 12:35:44 +10:00
private bool Valid ( EntityUid uid , EntityUid userUid , EntityUid usingUid , bool anchoring , AnchorableComponent ? anchorable = null , ToolComponent ? usingTool = null )
2021-11-09 08:08:30 +01:00
{
2023-02-24 19:01:25 -05:00
if ( ! Resolve ( uid , ref anchorable ) )
2021-11-09 08:08:30 +01:00
return false ;
if ( ! Resolve ( usingUid , ref usingTool ) )
return false ;
BaseAnchoredAttemptEvent attempt =
anchoring ? new AnchorAttemptEvent ( userUid , usingUid ) : new UnanchorAttemptEvent ( userUid , usingUid ) ;
// Need to cast the event or it will be raised as BaseAnchoredAttemptEvent.
if ( anchoring )
2022-10-30 02:48:53 -04:00
RaiseLocalEvent ( uid , ( AnchorAttemptEvent ) attempt ) ;
2021-11-09 08:08:30 +01:00
else
2022-10-30 02:48:53 -04:00
RaiseLocalEvent ( uid , ( UnanchorAttemptEvent ) attempt ) ;
2021-11-09 08:08:30 +01:00
2022-09-11 06:30:10 +00:00
anchorable . Delay + = attempt . Delay ;
2022-10-30 02:48:53 -04:00
return ! attempt . Cancelled ;
2021-11-09 08:08:30 +01:00
}
/// <summary>
/// Tries to anchor the entity.
/// </summary>
/// <returns>true if anchored, false otherwise</returns>
2022-05-18 12:35:44 +10:00
private void TryAnchor ( EntityUid uid , EntityUid userUid , EntityUid usingUid ,
2021-11-09 08:08:30 +01:00
AnchorableComponent ? anchorable = null ,
TransformComponent ? transform = null ,
SharedPullableComponent ? pullable = null ,
ToolComponent ? usingTool = null )
{
2022-08-11 12:11:29 +10:00
if ( ! Resolve ( uid , ref anchorable , ref transform ) )
return ;
2021-11-09 08:08:30 +01:00
// Optional resolves.
2022-01-11 07:43:58 +11:00
Resolve ( uid , ref pullable , false ) ;
2021-11-09 08:08:30 +01:00
2022-08-11 12:11:29 +10:00
if ( ! Resolve ( usingUid , ref usingTool ) )
return ;
if ( ! Valid ( uid , userUid , usingUid , true , anchorable , usingTool ) )
return ;
2021-11-09 08:08:30 +01:00
2022-08-11 12:11:29 +10:00
if ( TryComp < PhysicsComponent > ( uid , out var anchorBody ) & &
! TileFree ( transform . Coordinates , anchorBody ) )
{
2022-12-19 10:41:47 +13:00
_popup . PopupEntity ( Loc . GetString ( "anchorable-occupied" ) , uid , userUid ) ;
2022-08-11 12:11:29 +10:00
return ;
}
2021-11-09 08:08:30 +01:00
2023-02-24 19:01:25 -05:00
var toolEvData = new ToolEventData ( new TryAnchorCompletedEvent ( userUid , usingUid ) , targetEntity : uid ) ;
_tool . UseTool ( usingUid , userUid , uid , anchorable . Delay , usingTool . Qualities , toolEvData ) ;
2021-11-09 08:08:30 +01:00
}
/// <summary>
/// Tries to unanchor the entity.
/// </summary>
/// <returns>true if unanchored, false otherwise</returns>
2022-05-18 12:35:44 +10:00
private void TryUnAnchor ( EntityUid uid , EntityUid userUid , EntityUid usingUid ,
2021-11-09 08:08:30 +01:00
AnchorableComponent ? anchorable = null ,
TransformComponent ? transform = null ,
ToolComponent ? usingTool = null )
{
2023-02-24 19:01:25 -05:00
if ( ! Resolve ( uid , ref anchorable , ref transform ) )
2022-05-18 12:35:44 +10:00
return ;
2021-11-09 08:08:30 +01:00
2023-02-24 19:01:25 -05:00
if ( ! Resolve ( usingUid , ref usingTool ) )
return ;
2021-11-09 08:08:30 +01:00
2023-02-24 19:01:25 -05:00
if ( ! Valid ( uid , userUid , usingUid , false ) )
return ;
2022-03-25 22:10:43 +01:00
2023-02-24 19:01:25 -05:00
var toolEvData = new ToolEventData ( new TryUnanchorCompletedEvent ( userUid , usingUid ) , targetEntity : uid ) ;
_tool . UseTool ( usingUid , userUid , uid , anchorable . Delay , usingTool . Qualities , toolEvData ) ;
2021-11-09 08:08:30 +01:00
}
/// <summary>
/// Tries to toggle the anchored status of this component's owner.
/// </summary>
/// <returns>true if toggled, false otherwise</returns>
2022-05-18 12:35:44 +10:00
public override void TryToggleAnchor ( EntityUid uid , EntityUid userUid , EntityUid usingUid ,
2021-11-09 08:08:30 +01:00
AnchorableComponent ? anchorable = null ,
TransformComponent ? transform = null ,
SharedPullableComponent ? pullable = null ,
ToolComponent ? usingTool = null )
{
if ( ! Resolve ( uid , ref transform ) )
2022-05-18 12:35:44 +10:00
return ;
if ( transform . Anchored )
{
TryUnAnchor ( uid , userUid , usingUid , anchorable , transform , usingTool ) ;
2022-12-15 12:33:27 -06:00
// Log unanchor attempt
_adminLogger . Add ( LogType . Anchor , LogImpact . Low , $"{ToPrettyString(userUid):user} is trying to unanchor {ToPrettyString(uid):entity} from {transform.Coordinates:targetlocation}" ) ;
2022-05-18 12:35:44 +10:00
}
else
{
TryAnchor ( uid , userUid , usingUid , anchorable , transform , pullable , usingTool ) ;
2022-12-15 12:33:27 -06:00
// Log anchor attempt
_adminLogger . Add ( LogType . Anchor , LogImpact . Low , $"{ToPrettyString(userUid):user} is trying to anchor {ToPrettyString(uid):entity} to {transform.Coordinates:targetlocation}" ) ;
2022-05-18 12:35:44 +10:00
}
}
private abstract class AnchorEvent : EntityEventArgs
{
public EntityUid User ;
public EntityUid Using ;
2022-12-08 19:17:56 -06:00
protected AnchorEvent ( EntityUid userUid , EntityUid usingUid )
{
User = userUid ;
Using = usingUid ;
}
2022-05-18 12:35:44 +10:00
}
private sealed class TryUnanchorCompletedEvent : AnchorEvent
{
2022-12-08 19:17:56 -06:00
public TryUnanchorCompletedEvent ( EntityUid userUid , EntityUid usingUid ) : base ( userUid , usingUid )
{
}
2022-05-18 12:35:44 +10:00
}
private sealed class TryAnchorCompletedEvent : AnchorEvent
{
2022-12-08 19:17:56 -06:00
public TryAnchorCompletedEvent ( EntityUid userUid , EntityUid usingUid ) : base ( userUid , usingUid )
{
}
2022-05-18 12:35:44 +10:00
}
2021-11-09 08:08:30 +01:00
}
}