#nullable enable using System.Collections.Generic; using System.Linq; using Content.Shared.Physics; using Content.Shared.Physics.Pull; using Robust.Shared.GameObjects; using Robust.Shared.Log; using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; namespace Content.Shared.Throwing { /// /// Handles throwing landing and collisions. /// public class ThrownItemSystem : EntitySystem { private List _throwCollide = new(); private const string ThrowingFixture = "throw-fixture"; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(HandleSleep); SubscribeLocalEvent(HandleCollision); SubscribeLocalEvent(PreventCollision); SubscribeLocalEvent(ThrowItem); SubscribeLocalEvent(LandItem); SubscribeLocalEvent(HandlePullStarted); } private void LandItem(EntityUid uid, ThrownItemComponent component, LandEvent args) { if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) return; var fixture = physicsComponent.GetFixture(ThrowingFixture); if (fixture == null) { Logger.Error($"Tried to remove throwing fixture for {component.Owner} but none found?"); return; } physicsComponent.RemoveFixture(fixture); } private void ThrowItem(EntityUid uid, ThrownItemComponent component, ThrownEvent args) { if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent) || physicsComponent.Fixtures.Count != 1) return; if (physicsComponent.GetFixture(ThrowingFixture) != null) { Logger.Error($"Found existing throwing fixture on {component.Owner}"); return; } var shape = physicsComponent.Fixtures[0].Shape; var fixture = new Fixture(physicsComponent, shape) {CollisionLayer = (int) CollisionGroup.ThrownItem, Hard = false, ID = ThrowingFixture}; physicsComponent.AddFixture(fixture); } private void HandleCollision(EntityUid uid, ThrownItemComponent component, StartCollideEvent args) { var thrower = component.Thrower; var otherBody = args.OtherFixture.Body; if (otherBody.Owner == thrower) return; ThrowCollideInteraction(thrower, args.OurFixture.Body, otherBody); } private void PreventCollision(EntityUid uid, ThrownItemComponent component, PreventCollideEvent args) { if (args.BodyB.Owner == component.Thrower) { args.Cancel(); } } private void HandleSleep(EntityUid uid, ThrownItemComponent thrownItem, PhysicsSleepMessage message) { LandComponent(thrownItem); } private void HandlePullStarted(PullStartedMessage message) { // TODO: this isn't directed so things have to be done the bad way if (message.Pulled.Owner.TryGetComponent(out ThrownItemComponent? thrownItem)) LandComponent(thrownItem); } private void LandComponent(ThrownItemComponent thrownItem) { if (thrownItem.Owner.Deleted) return; var user = thrownItem.Thrower; var landing = thrownItem.Owner; var coordinates = landing.Transform.Coordinates; // LandInteraction // TODO: Refactor these to system messages var landMsg = new LandEvent(user, landing, coordinates); RaiseLocalEvent(landing.Uid, landMsg); if (landMsg.Handled) { return; } var comps = landing.GetAllComponents().ToArray(); var landArgs = new LandEventArgs(user, coordinates); // Call Land on all components that implement the interface foreach (var comp in comps) { if (landing.Deleted) break; comp.Land(landArgs); } ComponentManager.RemoveComponent(landing.Uid, thrownItem); } /// /// Calls ThrowCollide on all components that implement the IThrowCollide interface /// on a thrown entity and the target entity it hit. /// public void ThrowCollideInteraction(IEntity? user, IPhysBody thrown, IPhysBody target) { // TODO: Just pass in the bodies directly var collideMsg = new ThrowCollideEvent(user, thrown.Owner, target.Owner); RaiseLocalEvent(collideMsg); if (collideMsg.Handled) { return; } var eventArgs = new ThrowCollideEventArgs(user, thrown.Owner, target.Owner); foreach (var comp in target.Owner.GetAllComponents()) { _throwCollide.Add(comp); } foreach (var collide in _throwCollide) { if (target.Owner.Deleted) break; collide.HitBy(eventArgs); } _throwCollide.Clear(); foreach (var comp in thrown.Owner.GetAllComponents()) { _throwCollide.Add(comp); } foreach (var collide in _throwCollide) { if (thrown.Owner.Deleted) break; collide.DoHit(eventArgs); } _throwCollide.Clear(); } } }