Files
tbd-station-14/Content.Shared/Throwing/ThrownItemSystem.cs
metalgearsloth 4da74d0ee4 Refactor disposals to ECS (#4418)
* ECS up disposals

Also significantly reduced its CPU usage.

* Make update significantly less S L O W

* Start units pressurised

* Client-side flush lerping

* Fix powered not toggling UI

* Fix flush button

* InteractUsing

* Minor optimisations

* Fix collisions

* Make visual state ECS

* Almost done with shared

* Most stuff moved

* Optimise item sleeping
2021-08-12 13:40:38 +10:00

164 lines
5.8 KiB
C#

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
{
/// <summary>
/// Handles throwing landing and collisions.
/// </summary>
public class ThrownItemSystem : EntitySystem
{
private List<IThrowCollide> _throwCollide = new();
private const string ThrowingFixture = "throw-fixture";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ThrownItemComponent, PhysicsSleepMessage>(HandleSleep);
SubscribeLocalEvent<ThrownItemComponent, StartCollideEvent>(HandleCollision);
SubscribeLocalEvent<ThrownItemComponent, PreventCollideEvent>(PreventCollision);
SubscribeLocalEvent<ThrownItemComponent, ThrownEvent>(ThrowItem);
SubscribeLocalEvent<ThrownItemComponent, LandEvent>(LandItem);
SubscribeLocalEvent<PullStartedMessage>(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;
}
Get<SharedBroadphaseSystem>().DestroyFixture(physicsComponent, 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;
Get<SharedBroadphaseSystem>().CreateFixture(physicsComponent, new Fixture(physicsComponent, shape) {CollisionLayer = (int) CollisionGroup.ThrownItem, Hard = false, ID = ThrowingFixture});
}
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<ILand>().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);
}
/// <summary>
/// Calls ThrowCollide on all components that implement the IThrowCollide interface
/// on a thrown entity and the target entity it hit.
/// </summary>
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(target.Owner.Uid, collideMsg);
if (collideMsg.Handled)
{
return;
}
var eventArgs = new ThrowCollideEventArgs(user, thrown.Owner, target.Owner);
foreach (var comp in target.Owner.GetAllComponents<IThrowCollide>())
{
_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<IThrowCollide>())
{
_throwCollide.Add(comp);
}
foreach (var collide in _throwCollide)
{
if (thrown.Owner.Deleted) break;
collide.DoHit(eventArgs);
}
_throwCollide.Clear();
}
}
}