2023-04-07 11:21:12 -07:00
|
|
|
using System.Linq;
|
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;
|
2023-11-14 04:52:18 -06:00
|
|
|
using Content.Shared.Gravity;
|
2021-05-31 17:13:40 +10:00
|
|
|
using Content.Shared.Physics;
|
2024-03-19 14:30:56 +11:00
|
|
|
using Content.Shared.Movement.Pulling.Events;
|
2021-03-08 04:09:59 +11:00
|
|
|
using Robust.Shared.Physics;
|
2022-09-14 17:26:26 +10:00
|
|
|
using Robust.Shared.Physics.Components;
|
|
|
|
|
using Robust.Shared.Physics.Events;
|
|
|
|
|
using Robust.Shared.Physics.Systems;
|
2023-10-06 17:43:54 -07:00
|
|
|
using Robust.Shared.Timing;
|
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-10-06 17:43:54 -07:00
|
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
2023-01-18 05:25:32 +11:00
|
|
|
[Dependency] private readonly SharedBroadphaseSystem _broadphase = 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!;
|
2023-11-14 04:52:18 -06:00
|
|
|
[Dependency] private readonly SharedGravitySystem _gravity = 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();
|
2023-10-06 17:43:54 -07:00
|
|
|
SubscribeLocalEvent<ThrownItemComponent, MapInitEvent>(OnMapInit);
|
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);
|
2024-01-06 13:38:37 +11:00
|
|
|
|
2021-05-27 11:47:39 +01:00
|
|
|
SubscribeLocalEvent<PullStartedMessage>(HandlePullStarted);
|
2021-03-08 04:09:59 +11:00
|
|
|
}
|
|
|
|
|
|
2023-10-06 17:43:54 -07:00
|
|
|
private void OnMapInit(EntityUid uid, ThrownItemComponent component, MapInitEvent args)
|
2021-10-12 21:58:11 +02:00
|
|
|
{
|
2023-10-06 17:43:54 -07:00
|
|
|
component.ThrownTime ??= _gameTiming.CurTime;
|
2021-10-12 21:58:11 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-04 09:32:17 +03:00
|
|
|
private void ThrowItem(EntityUid uid, ThrownItemComponent component, ref ThrownEvent @event)
|
2021-05-31 17:13:40 +10:00
|
|
|
{
|
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
|
|
|
{
|
2023-08-15 09:49:59 +10:00
|
|
|
if (!args.OtherFixture.Hard)
|
2022-05-17 19:24:58 -07:00
|
|
|
return;
|
|
|
|
|
|
2023-08-02 12:30:04 +03:00
|
|
|
if (args.OtherEntity == component.Thrower)
|
2023-05-09 19:21:26 +12:00
|
|
|
return;
|
2021-05-31 17:13:40 +10:00
|
|
|
|
2023-08-02 12:30:04 +03:00
|
|
|
ThrowCollideInteraction(component, args.OurEntity, args.OtherEntity);
|
2021-05-31 17:13:40 +10:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2023-06-03 00:20:09 +12:00
|
|
|
if (args.OtherEntity == component.Thrower)
|
2021-05-31 17:13:40 +10:00
|
|
|
{
|
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
|
2024-03-19 14:30:56 +11:00
|
|
|
if (EntityManager.TryGetComponent(message.PulledUid, out ThrownItemComponent? thrownItemComponent))
|
|
|
|
|
StopThrow(message.PulledUid, thrownItemComponent);
|
2021-05-27 11:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
2023-08-15 09:49:59 +10:00
|
|
|
public void StopThrow(EntityUid uid, ThrownItemComponent thrownItemComponent)
|
2021-05-27 11:47:39 +01:00
|
|
|
{
|
2023-08-15 09:49:59 +10:00
|
|
|
if (TryComp<PhysicsComponent>(uid, out var physics))
|
|
|
|
|
{
|
2024-03-25 02:37:25 -04:00
|
|
|
_physics.SetBodyStatus(uid, physics, BodyStatus.OnGround);
|
2024-01-06 13:38:37 +11:00
|
|
|
|
|
|
|
|
if (physics.Awake)
|
|
|
|
|
_broadphase.RegenerateContacts(uid, physics);
|
2023-08-15 09:49:59 +10: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-08-23 18:55:58 +10:00
|
|
|
_fixtures.DestroyFixture(uid, ThrowingFixture, fixture, manager: manager);
|
2021-09-29 00:16:00 +10:00
|
|
|
}
|
|
|
|
|
}
|
2021-09-10 22:59:50 +10:00
|
|
|
|
2023-11-14 04:52:18 -06: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-08-02 15:44:27 +03:00
|
|
|
public void LandComponent(EntityUid uid, ThrownItemComponent thrownItem, PhysicsComponent physics, bool playSound)
|
2021-09-29 00:16:00 +10:00
|
|
|
{
|
2023-11-14 04:52:18 -06:00
|
|
|
if (thrownItem.Landed || thrownItem.Deleted || _gravity.IsWeightless(uid) || Deleted(uid))
|
2023-03-11 19:26:01 +11:00
|
|
|
return;
|
2021-09-10 22:59:50 +10:00
|
|
|
|
2023-10-06 17:43:54 -07:00
|
|
|
thrownItem.Landed = true;
|
|
|
|
|
|
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)
|
2023-08-02 15:44:27 +03:00
|
|
|
_adminLogger.Add(LogType.Landed, LogImpact.Low, $"{ToPrettyString(uid):entity} thrown by {ToPrettyString(thrownItem.Thrower.Value):thrower} landed.");
|
2021-11-24 16:52:31 -06:00
|
|
|
|
2023-05-28 23:22:44 +10:00
|
|
|
_broadphase.RegenerateContacts(uid, physics);
|
2023-05-19 17:10:31 +10:00
|
|
|
var landEvent = new LandEvent(thrownItem.Thrower, playSound);
|
2023-08-02 15:44:27 +03:00
|
|
|
RaiseLocalEvent(uid, 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-08-02 12:30:04 +03:00
|
|
|
public void ThrowCollideInteraction(ThrownItemComponent component, EntityUid thrown, EntityUid target)
|
2021-03-08 04:09:59 +11:00
|
|
|
{
|
2023-08-02 12:30:04 +03:00
|
|
|
if (component.Thrower is not null)
|
2022-05-28 23:41:17 -07:00
|
|
|
_adminLogger.Add(LogType.ThrowHit, LogImpact.Low,
|
2023-08-02 12:30:04 +03:00
|
|
|
$"{ToPrettyString(thrown):thrown} thrown by {ToPrettyString(component.Thrower.Value):thrower} hit {ToPrettyString(target):target}.");
|
|
|
|
|
|
|
|
|
|
RaiseLocalEvent(target, new ThrowHitByEvent(thrown, target, component), true);
|
|
|
|
|
RaiseLocalEvent(thrown, new ThrowDoHitEvent(thrown, target, component), true);
|
2021-03-08 04:09:59 +11:00
|
|
|
}
|
2023-10-06 17:43:54 -07:00
|
|
|
|
|
|
|
|
public override void Update(float frameTime)
|
|
|
|
|
{
|
|
|
|
|
base.Update(frameTime);
|
|
|
|
|
|
|
|
|
|
var query = EntityQueryEnumerator<ThrownItemComponent, PhysicsComponent>();
|
|
|
|
|
while (query.MoveNext(out var uid, out var thrown, out var physics))
|
|
|
|
|
{
|
|
|
|
|
if (thrown.LandTime <= _gameTiming.CurTime)
|
|
|
|
|
{
|
|
|
|
|
LandComponent(uid, thrown, physics, thrown.PlayLandSound);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-08 11:03:53 +02:00
|
|
|
var stopThrowTime = thrown.LandTime ?? thrown.ThrownTime;
|
2023-10-06 17:43:54 -07:00
|
|
|
if (stopThrowTime <= _gameTiming.CurTime)
|
|
|
|
|
{
|
|
|
|
|
StopThrow(uid, thrown);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-08 04:09:59 +11:00
|
|
|
}
|
|
|
|
|
}
|