2021-11-24 16:52:31 -06:00
using Content.Shared.Administration.Logs ;
2021-11-28 14:56:53 +01:00
using Content.Shared.Database ;
2021-08-31 22:46:11 +10:00
using Content.Shared.Hands.Components ;
2021-05-31 17:13:40 +10:00
using Content.Shared.Physics ;
2021-06-09 22:19:39 +02:00
using Content.Shared.Physics.Pull ;
2021-08-31 22:46:11 +10:00
using Robust.Shared.Containers ;
2021-10-12 21:58:11 +02:00
using Robust.Shared.GameStates ;
2021-03-08 04:09:59 +11:00
using Robust.Shared.Physics ;
2021-05-27 11:47:39 +01:00
using Robust.Shared.Physics.Dynamics ;
2022-01-21 01:38:35 -08:00
using System.Linq ;
2023-03-11 19:26:01 +11:00
using Content.Shared.Sound.Components ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Components ;
using Robust.Shared.Physics.Events ;
using Robust.Shared.Physics.Systems ;
2021-03-08 04:09:59 +11:00
2021-06-09 22:19:39 +02:00
namespace Content.Shared.Throwing
2021-03-08 04:09:59 +11:00
{
/// <summary>
/// Handles throwing landing and collisions.
/// </summary>
2021-12-01 18:32:37 +11:00
public sealed class ThrownItemSystem : EntitySystem
2021-03-08 04:09:59 +11:00
{
2022-05-28 23:41:17 -07:00
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default ! ;
2023-01-18 05:25:32 +11:00
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default ! ;
[Dependency] private readonly SharedContainerSystem _containerSystem = default ! ;
2021-12-01 18:32:37 +11:00
[Dependency] private readonly FixtureSystem _fixtures = default ! ;
2023-01-18 05:25:32 +11:00
[Dependency] private readonly SharedPhysicsSystem _physics = default ! ;
2021-08-23 15:02:03 +10:00
2021-05-31 17:13:40 +10:00
private const string ThrowingFixture = "throw-fixture" ;
2021-03-08 04:09:59 +11:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-04-24 13:54:25 +10:00
SubscribeLocalEvent < ThrownItemComponent , PhysicsSleepEvent > ( OnSleep ) ;
2021-05-31 17:13:40 +10:00
SubscribeLocalEvent < ThrownItemComponent , StartCollideEvent > ( HandleCollision ) ;
SubscribeLocalEvent < ThrownItemComponent , PreventCollideEvent > ( PreventCollision ) ;
SubscribeLocalEvent < ThrownItemComponent , ThrownEvent > ( ThrowItem ) ;
2021-10-12 21:58:11 +02:00
SubscribeLocalEvent < ThrownItemComponent , ComponentGetState > ( OnGetState ) ;
SubscribeLocalEvent < ThrownItemComponent , ComponentHandleState > ( OnHandleState ) ;
2021-05-27 11:47:39 +01:00
SubscribeLocalEvent < PullStartedMessage > ( HandlePullStarted ) ;
2021-03-08 04:09:59 +11:00
}
2021-10-12 21:58:11 +02:00
private void OnGetState ( EntityUid uid , ThrownItemComponent component , ref ComponentGetState args )
{
2021-12-03 15:53:09 +01:00
args . State = new ThrownItemComponentState ( component . Thrower ) ;
2021-10-12 21:58:11 +02:00
}
private void OnHandleState ( EntityUid uid , ThrownItemComponent component , ref ComponentHandleState args )
{
2021-12-05 18:09:01 +01:00
if ( args . Current is not ThrownItemComponentState { Thrower : not null } state | |
! state . Thrower . Value . IsValid ( ) )
{
2021-10-12 21:58:11 +02:00
return ;
2021-12-05 18:09:01 +01:00
}
2021-10-12 21:58:11 +02:00
2021-12-05 18:09:01 +01:00
component . Thrower = state . Thrower . Value ;
2021-10-12 21:58:11 +02:00
}
2021-05-31 17:13:40 +10:00
private void ThrowItem ( EntityUid uid , ThrownItemComponent component , ThrownEvent args )
{
2023-01-15 15:38:59 +11:00
if ( ! EntityManager . TryGetComponent ( uid , out FixturesComponent ? fixturesComponent ) | |
fixturesComponent . Fixtures . Count ! = 1 | |
! TryComp < PhysicsComponent > ( uid , out var body ) )
2021-05-31 17:13:40 +10:00
{
return ;
}
2023-01-15 15:38:59 +11:00
2022-01-21 01:38:35 -08:00
var fixture = fixturesComponent . Fixtures . Values . First ( ) ;
var shape = fixture . Shape ;
2023-01-15 15:38:59 +11:00
_fixtures . TryCreateFixture ( uid , shape , ThrowingFixture , hard : false , collisionMask : ( int ) CollisionGroup . ThrownItem , manager : fixturesComponent , body : body ) ;
2021-05-31 17:13:40 +10:00
}
2022-09-14 17:26:26 +10:00
private void HandleCollision ( EntityUid uid , ThrownItemComponent component , ref StartCollideEvent args )
2021-05-31 17:13:40 +10:00
{
2022-05-17 19:24:58 -07:00
if ( args . OtherFixture . Hard = = false )
return ;
2021-05-31 17:13:40 +10:00
var thrower = component . Thrower ;
var otherBody = args . OtherFixture . Body ;
if ( otherBody . Owner = = thrower ) return ;
ThrowCollideInteraction ( thrower , args . OurFixture . Body , otherBody ) ;
}
2022-09-14 17:26:26 +10:00
private void PreventCollision ( EntityUid uid , ThrownItemComponent component , ref PreventCollideEvent args )
2021-05-31 17:13:40 +10:00
{
if ( args . BodyB . Owner = = component . Thrower )
{
2022-09-14 17:26:26 +10:00
args . Cancelled = true ;
2021-05-31 17:13:40 +10:00
}
}
2022-04-24 13:54:25 +10:00
private void OnSleep ( EntityUid uid , ThrownItemComponent thrownItem , ref PhysicsSleepEvent @event )
2021-03-08 04:09:59 +11:00
{
2021-09-29 00:16:00 +10:00
StopThrow ( uid , thrownItem ) ;
2021-03-08 04:09:59 +11:00
}
2021-05-27 11:47:39 +01:00
private void HandlePullStarted ( PullStartedMessage message )
2021-03-08 04:09:59 +11:00
{
2021-05-27 11:47:39 +01:00
// TODO: this isn't directed so things have to be done the bad way
2021-12-03 15:53:09 +01:00
if ( EntityManager . TryGetComponent ( message . Pulled . Owner , out ThrownItemComponent ? thrownItemComponent ) )
StopThrow ( message . Pulled . Owner , thrownItemComponent ) ;
2021-05-27 11:47:39 +01:00
}
2021-09-29 00:16:00 +10:00
private void StopThrow ( EntityUid uid , ThrownItemComponent thrownItemComponent )
2021-05-27 11:47:39 +01:00
{
2023-01-15 15:38:59 +11:00
if ( EntityManager . TryGetComponent ( uid , out FixturesComponent ? manager ) )
2021-09-29 00:16:00 +10:00
{
2023-01-15 15:38:59 +11:00
var fixture = _fixtures . GetFixtureOrNull ( uid , ThrowingFixture , manager : manager ) ;
2021-03-08 04:09:59 +11:00
2021-09-29 00:16:00 +10:00
if ( fixture ! = null )
{
2023-01-15 15:38:59 +11:00
_fixtures . DestroyFixture ( uid , fixture , manager : manager ) ;
2021-09-29 00:16:00 +10:00
}
}
2021-09-10 22:59:50 +10:00
2022-06-22 09:53:41 +10:00
EntityManager . EventBus . RaiseLocalEvent ( uid , new StopThrowEvent { User = thrownItemComponent . Thrower } , true ) ;
2021-09-29 00:16:00 +10:00
EntityManager . RemoveComponent < ThrownItemComponent > ( uid ) ;
}
2021-09-10 22:59:50 +10:00
2023-03-11 19:26:01 +11:00
public void LandComponent ( EntityUid uid , ThrownItemComponent thrownItem , PhysicsComponent physics )
2021-09-29 00:16:00 +10:00
{
2023-01-18 05:25:32 +11:00
_physics . SetBodyStatus ( physics , BodyStatus . OnGround ) ;
2023-03-11 19:26:01 +11:00
if ( thrownItem . Deleted | | Deleted ( uid ) | | _containerSystem . IsEntityInContainer ( uid ) )
return ;
2021-09-10 22:59:50 +10:00
2023-03-11 19:26:01 +11:00
var landing = uid ;
2021-09-10 22:59:50 +10:00
// Unfortunately we can't check for hands containers as they have specific names.
2023-03-11 19:26:01 +11:00
if ( uid . TryGetContainerMan ( out var containerManager ) & &
2021-12-07 22:22:34 +11:00
EntityManager . HasComponent < SharedHandsComponent > ( containerManager . Owner ) )
2021-09-10 22:59:50 +10:00
{
2021-12-03 15:53:09 +01:00
EntityManager . RemoveComponent ( landing , thrownItem ) ;
2021-09-10 22:59:50 +10:00
return ;
}
2021-11-24 16:52:31 -06:00
// Assume it's uninteresting if it has no thrower. For now anyway.
if ( thrownItem . Thrower is not null )
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . Landed , LogImpact . Low , $"{ToPrettyString(landing):entity} thrown by {ToPrettyString(thrownItem.Thrower.Value):thrower} landed." ) ;
2021-11-24 16:52:31 -06:00
2023-01-18 05:25:32 +11:00
_broadphase . RegenerateContacts ( physics ) ;
var landEvent = new LandEvent ( thrownItem . Thrower ) ;
RaiseLocalEvent ( landing , ref landEvent ) ;
2021-03-08 04:09:59 +11:00
}
/// <summary>
2021-08-21 09:18:23 +02:00
/// Raises collision events on the thrown and target entities.
2021-03-08 04:09:59 +11:00
/// </summary>
2023-01-03 17:45:18 +11:00
public void ThrowCollideInteraction ( EntityUid ? user , PhysicsComponent thrown , PhysicsComponent target )
2021-03-08 04:09:59 +11:00
{
2021-11-29 02:34:44 +13:00
if ( user is not null )
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . ThrowHit , LogImpact . Low ,
2021-12-11 16:00:10 +01:00
$"{ToPrettyString(thrown.Owner):thrown} thrown by {ToPrettyString(user.Value):thrower} hit {ToPrettyString(target.Owner):target}." ) ;
2021-03-08 04:09:59 +11:00
// TODO: Just pass in the bodies directly
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( target . Owner , new ThrowHitByEvent ( user , thrown . Owner , target . Owner ) , true ) ;
RaiseLocalEvent ( thrown . Owner , new ThrowDoHitEvent ( user , thrown . Owner , target . Owner ) , true ) ;
2021-03-08 04:09:59 +11:00
}
}
}