2021-06-09 22:19:39 +02:00
using Content.Server.DoAfter ;
2021-09-26 15:18:45 +02:00
using Content.Server.Popups ;
2021-06-09 22:19:39 +02:00
using Content.Shared.ActionBlocker ;
using Content.Shared.Body.Components ;
using Content.Shared.Body.Part ;
using Content.Shared.Climbing ;
using Content.Shared.DragDrop ;
2022-02-17 15:40:03 +13:00
using Content.Shared.Interaction ;
2022-02-15 17:06:52 +13:00
using Content.Shared.Interaction.Events ;
2021-09-26 15:18:45 +02:00
using Content.Shared.Popups ;
2022-01-09 20:10:36 -08:00
using Robust.Shared.Physics ;
2020-08-19 18:13:22 -04:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Climbing.Components
2020-08-19 18:13:22 -04:00
{
[RegisterComponent]
[ComponentReference(typeof(IClimbable))]
2022-02-16 00:23:23 -07:00
public sealed class ClimbableComponent : SharedClimbableComponent
2020-08-19 18:13:22 -04:00
{
2021-12-05 21:02:04 +01:00
[Dependency] private readonly IEntityManager _entities = default ! ;
2020-08-19 18:13:22 -04:00
/// <summary>
/// The time it takes to climb onto the entity.
/// </summary>
[ViewVariables]
2021-03-05 01:08:38 +01:00
[DataField("delay")]
private float _climbDelay = 0.8f ;
2020-08-19 18:13:22 -04:00
2021-06-19 19:41:26 -07:00
protected override void Initialize ( )
2020-08-19 18:13:22 -04:00
{
base . Initialize ( ) ;
2021-01-11 22:14:01 +11:00
if ( ! Owner . EnsureComponent ( out PhysicsComponent _ ) )
{
2021-12-05 21:02:04 +01:00
Logger . Warning ( $"Entity {_entities.GetComponent<MetaDataComponent>(Owner).EntityName} at {_entities.GetComponent<TransformComponent>(Owner).MapPosition} didn't have a {nameof(PhysicsComponent)}" ) ;
2021-01-11 22:14:01 +11:00
}
2020-08-19 18:13:22 -04:00
}
2021-05-22 21:06:40 -07:00
public override bool CanDragDropOn ( DragDropEvent eventArgs )
2020-08-19 18:13:22 -04:00
{
2021-01-11 22:14:01 +11:00
if ( ! base . CanDragDropOn ( eventArgs ) )
return false ;
2020-08-25 13:45:00 +02:00
string reason ;
bool canVault ;
2020-08-19 18:13:22 -04:00
2020-10-14 15:24:07 +02:00
if ( eventArgs . User = = eventArgs . Dragged )
2020-08-25 13:45:00 +02:00
canVault = CanVault ( eventArgs . User , eventArgs . Target , out reason ) ;
else
2020-10-14 15:24:07 +02:00
canVault = CanVault ( eventArgs . User , eventArgs . Dragged , eventArgs . Target , out reason ) ;
2020-08-25 13:45:00 +02:00
if ( ! canVault )
2020-09-01 12:34:53 +02:00
eventArgs . User . PopupMessage ( reason ) ;
2020-08-25 13:45:00 +02:00
return canVault ;
}
/// <summary>
/// Checks if the user can vault the target
/// </summary>
/// <param name="user">The entity that wants to vault</param>
/// <param name="target">The object that is being vaulted</param>
/// <param name="reason">The reason why it cant be dropped</param>
/// <returns></returns>
2021-12-05 18:09:01 +01:00
private bool CanVault ( EntityUid user , EntityUid target , out string reason )
2020-08-25 13:45:00 +02:00
{
2022-02-15 17:06:52 +13:00
if ( ! EntitySystem . Get < ActionBlockerSystem > ( ) . CanInteract ( user , target ) )
2020-08-25 13:45:00 +02:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-interact" ) ;
2020-08-19 18:13:22 -04:00
return false ;
}
2021-12-05 21:02:04 +01:00
if ( ! _entities . HasComponent < ClimbingComponent > ( user ) | |
! _entities . TryGetComponent ( user , out SharedBodyComponent ? body ) )
2020-08-19 18:13:22 -04:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-climb" ) ;
2020-08-25 13:45:00 +02:00
return false ;
}
2020-08-19 18:13:22 -04:00
2021-04-05 14:54:51 +02:00
if ( ! body . HasPartOfType ( BodyPartType . Leg ) | |
! body . HasPartOfType ( BodyPartType . Foot ) )
2020-08-25 13:45:00 +02:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-climb" ) ;
2020-08-25 13:45:00 +02:00
return false ;
}
2020-08-19 18:13:22 -04:00
2022-02-17 15:40:03 +13:00
if ( ! EntitySystem . Get < SharedInteractionSystem > ( ) . InRangeUnobstructed ( user , target , Range ) )
2020-08-25 13:45:00 +02:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-reach" ) ;
2020-08-25 13:45:00 +02:00
return false ;
}
2020-08-19 18:13:22 -04:00
2020-08-25 13:45:00 +02:00
reason = string . Empty ;
return true ;
}
2020-08-19 18:13:22 -04:00
2020-08-25 13:45:00 +02:00
/// <summary>
/// Checks if the user can vault the dragged entity onto the the target
/// </summary>
/// <param name="user">The user that wants to vault the entity</param>
/// <param name="dragged">The entity that is being vaulted</param>
/// <param name="target">The object that is being vaulted onto</param>
/// <param name="reason">The reason why it cant be dropped</param>
/// <returns></returns>
2021-12-05 18:09:01 +01:00
private bool CanVault ( EntityUid user , EntityUid dragged , EntityUid target , out string reason )
2020-08-25 13:45:00 +02:00
{
2022-02-15 17:06:52 +13:00
if ( ! EntitySystem . Get < ActionBlockerSystem > ( ) . CanInteract ( user , dragged ) )
{
reason = Loc . GetString ( "comp-climbable-cant-interact" ) ;
return false ;
}
// CanInteract() doesn't support checking a second "target" entity.
// Doing so manually:
var ev = new GettingInteractedWithAttemptEvent ( user , target ) ;
_entities . EventBus . RaiseLocalEvent ( target , ev ) ;
if ( ev . Cancelled )
2020-08-19 18:13:22 -04:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-interact" ) ;
2020-08-25 13:45:00 +02:00
return false ;
}
2020-08-19 18:13:22 -04:00
2021-12-26 15:32:45 +13:00
if ( ! _entities . HasComponent < ClimbingComponent > ( dragged ) )
2020-08-25 13:45:00 +02:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-climb" ) ;
2020-08-25 13:45:00 +02:00
return false ;
}
2020-08-19 18:13:22 -04:00
2021-12-05 18:09:01 +01:00
bool Ignored ( EntityUid entity ) = > entity = = target | | entity = = user | | entity = = dragged ;
2020-08-19 18:13:22 -04:00
2022-02-17 15:40:03 +13:00
var sys = EntitySystem . Get < SharedInteractionSystem > ( ) ;
if ( ! sys . InRangeUnobstructed ( user , target , Range , predicate : Ignored ) | |
! sys . InRangeUnobstructed ( user , dragged , Range , predicate : Ignored ) )
2020-08-25 13:45:00 +02:00
{
2021-03-15 21:02:56 +03:00
reason = Loc . GetString ( "comp-climbable-cant-reach" ) ;
2020-08-25 13:45:00 +02:00
return false ;
2020-08-19 18:13:22 -04:00
}
2020-08-25 13:45:00 +02:00
reason = string . Empty ;
2020-08-19 18:13:22 -04:00
return true ;
}
2021-05-22 21:06:40 -07:00
public override bool DragDropOn ( DragDropEvent eventArgs )
2020-08-19 18:13:22 -04:00
{
2020-10-14 15:24:07 +02:00
if ( eventArgs . User = = eventArgs . Dragged )
2020-08-19 18:13:22 -04:00
{
2022-01-22 05:52:35 -07:00
TryClimb ( eventArgs . User , eventArgs . Target ) ;
2020-08-19 18:13:22 -04:00
}
else
{
2022-01-22 05:52:35 -07:00
TryMoveEntity ( eventArgs . User , eventArgs . Dragged , eventArgs . Target ) ;
2020-08-19 18:13:22 -04:00
}
return true ;
}
2022-01-22 05:52:35 -07:00
private async void TryMoveEntity ( EntityUid user , EntityUid entityToMove , EntityUid climbable )
2020-08-19 18:13:22 -04:00
{
var doAfterEventArgs = new DoAfterEventArgs ( user , _climbDelay , default , entityToMove )
{
BreakOnTargetMove = true ,
BreakOnUserMove = true ,
BreakOnDamage = true ,
BreakOnStun = true
} ;
2021-07-04 13:32:24 +02:00
var result = await EntitySystem . Get < DoAfterSystem > ( ) . WaitDoAfter ( doAfterEventArgs ) ;
2020-08-19 18:13:22 -04:00
2022-01-09 20:10:36 -08:00
if ( result ! = DoAfterStatus . Cancelled & & _entities . TryGetComponent ( entityToMove , out FixturesComponent ? body ) & & body . FixtureCount > = 1 )
2020-08-19 18:13:22 -04:00
{
2021-12-05 21:02:04 +01:00
var entityPos = _entities . GetComponent < TransformComponent > ( entityToMove ) . WorldPosition ;
2021-03-08 04:09:59 +11:00
2021-12-05 21:02:04 +01:00
var direction = ( _entities . GetComponent < TransformComponent > ( Owner ) . WorldPosition - entityPos ) . Normalized ;
var endPoint = _entities . GetComponent < TransformComponent > ( Owner ) . WorldPosition ;
2020-08-19 18:13:22 -04:00
2021-12-05 21:02:04 +01:00
var climbMode = _entities . GetComponent < ClimbingComponent > ( entityToMove ) ;
2020-08-19 18:13:22 -04:00
climbMode . IsClimbing = true ;
if ( MathF . Abs ( direction . X ) < 0.6f ) // user climbed mostly vertically so lets make it a clean straight line
{
2021-03-08 04:09:59 +11:00
endPoint = new Vector2 ( entityPos . X , endPoint . Y ) ;
2020-08-19 18:13:22 -04:00
}
else if ( MathF . Abs ( direction . Y ) < 0.6f ) // user climbed mostly horizontally so lets make it a clean straight line
{
2021-03-08 04:09:59 +11:00
endPoint = new Vector2 ( endPoint . X , entityPos . Y ) ;
2020-08-19 18:13:22 -04:00
}
2021-03-08 04:09:59 +11:00
climbMode . TryMoveTo ( entityPos , endPoint ) ;
2020-08-19 18:13:22 -04:00
// we may potentially need additional logic since we're forcing a player onto a climbable
// there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for
2022-01-22 05:52:35 -07:00
_entities . EventBus . RaiseLocalEvent ( entityToMove , new StartClimbEvent ( climbable ) , false ) ;
_entities . EventBus . RaiseLocalEvent ( climbable , new ClimbedOnEvent ( entityToMove ) , false ) ;
2021-03-15 21:02:56 +03:00
var othersMessage = Loc . GetString ( "comp-climbable-user-climbs-force-other" ,
( "user" , user ) , ( "moved-user" , entityToMove ) , ( "climbable" , Owner ) ) ;
2020-08-30 11:28:46 +02:00
user . PopupMessageOtherClients ( othersMessage ) ;
2021-03-15 21:02:56 +03:00
var selfMessage = Loc . GetString ( "comp-climbable-user-climbs-force" , ( "moved-user" , entityToMove ) , ( "climbable" , Owner ) ) ;
2020-08-30 11:28:46 +02:00
user . PopupMessage ( selfMessage ) ;
2020-08-19 18:13:22 -04:00
}
}
2022-01-22 05:52:35 -07:00
public async void TryClimb ( EntityUid user , EntityUid climbable )
2020-08-19 18:13:22 -04:00
{
2021-12-05 21:02:04 +01:00
if ( ! _entities . TryGetComponent ( user , out ClimbingComponent ? climbingComponent ) | | climbingComponent . IsClimbing )
2020-10-12 02:12:46 +11:00
return ;
2020-08-19 18:13:22 -04:00
var doAfterEventArgs = new DoAfterEventArgs ( user , _climbDelay , default , Owner )
{
BreakOnTargetMove = true ,
BreakOnUserMove = true ,
BreakOnDamage = true ,
BreakOnStun = true
} ;
2021-07-04 13:32:24 +02:00
var result = await EntitySystem . Get < DoAfterSystem > ( ) . WaitDoAfter ( doAfterEventArgs ) ;
2020-08-19 18:13:22 -04:00
2022-01-21 01:38:35 -08:00
if ( result ! = DoAfterStatus . Cancelled & & _entities . TryGetComponent ( user , out FixturesComponent ? fixtureComp ) & & fixtureComp . Fixtures . Count > = 1 )
2020-08-19 18:13:22 -04:00
{
2021-03-08 04:09:59 +11:00
// TODO: Remove the copy-paste code
2021-12-05 21:02:04 +01:00
var userPos = _entities . GetComponent < TransformComponent > ( user ) . WorldPosition ;
2021-03-08 04:09:59 +11:00
2021-12-05 21:02:04 +01:00
var direction = ( _entities . GetComponent < TransformComponent > ( Owner ) . WorldPosition - userPos ) . Normalized ;
var endPoint = _entities . GetComponent < TransformComponent > ( Owner ) . WorldPosition ;
2020-08-19 18:13:22 -04:00
2022-01-22 05:52:35 -07:00
_entities . EventBus . RaiseLocalEvent ( user , new StartClimbEvent ( climbable ) , false ) ;
_entities . EventBus . RaiseLocalEvent ( climbable , new ClimbedOnEvent ( user ) , false ) ;
2021-12-05 21:02:04 +01:00
var climbMode = _entities . GetComponent < ClimbingComponent > ( user ) ;
2020-08-19 18:13:22 -04:00
climbMode . IsClimbing = true ;
if ( MathF . Abs ( direction . X ) < 0.6f ) // user climbed mostly vertically so lets make it a clean straight line
{
2021-12-05 21:02:04 +01:00
endPoint = new Vector2 ( _entities . GetComponent < TransformComponent > ( user ) . WorldPosition . X , endPoint . Y ) ;
2020-08-19 18:13:22 -04:00
}
else if ( MathF . Abs ( direction . Y ) < 0.6f ) // user climbed mostly horizontally so lets make it a clean straight line
{
2021-12-05 21:02:04 +01:00
endPoint = new Vector2 ( endPoint . X , _entities . GetComponent < TransformComponent > ( user ) . WorldPosition . Y ) ;
2020-08-19 18:13:22 -04:00
}
2021-03-08 04:09:59 +11:00
climbMode . TryMoveTo ( userPos , endPoint ) ;
2020-08-19 18:13:22 -04:00
2021-03-15 21:02:56 +03:00
var othersMessage = Loc . GetString ( "comp-climbable-user-climbs-other" , ( "user" , user ) , ( "climbable" , Owner ) ) ;
2020-08-30 11:28:46 +02:00
user . PopupMessageOtherClients ( othersMessage ) ;
2020-08-19 18:13:22 -04:00
2021-03-15 21:02:56 +03:00
var selfMessage = Loc . GetString ( "comp-climbable-user-climbs" , ( "climbable" , Owner ) ) ;
2020-08-30 11:28:46 +02:00
user . PopupMessage ( selfMessage ) ;
2020-08-19 18:13:22 -04:00
}
}
}
}
2022-01-22 05:52:35 -07:00
/// <summary>
/// Raised on an entity when it is climbed on.
/// </summary>
2022-02-16 00:23:23 -07:00
public sealed class ClimbedOnEvent : EntityEventArgs
2022-01-22 05:52:35 -07:00
{
public EntityUid Climber ;
public ClimbedOnEvent ( EntityUid climber )
{
Climber = climber ;
}
}
/// <summary>
/// Raised on an entity when it successfully climbs on something.
/// </summary>
2022-02-16 00:23:23 -07:00
public sealed class StartClimbEvent : EntityEventArgs
2022-01-22 05:52:35 -07:00
{
public EntityUid Climbable ;
public StartClimbEvent ( EntityUid climbable )
{
Climbable = climbable ;
}
}