Skip to content
This repository was archived by the owner on Sep 13, 2022. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Content.Server/2G2C/Carrying/BeingCarriedComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Content.Server.Carrying
{
/// <summary>
/// Used so we can sub to events like VirtualItemDeleted
/// </summary>
[RegisterComponent]
public sealed class BeingCarriedComponent : Component
{
public EntityUid Carrier = default!;
}
}
16 changes: 16 additions & 0 deletions Content.Server/2G2C/Carrying/CarriableComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Threading;

namespace Content.Server.Carrying
{
[RegisterComponent]
public sealed class CarriableComponent : Component
{
public CancellationTokenSource? CancelToken;
/// <summary>
/// Number of free hands required
/// to carry the entity
/// </summary>
[DataField("freeHandsRequired")]
public int FreeHandsRequired = 2;
}
}
9 changes: 9 additions & 0 deletions Content.Server/2G2C/Carrying/CarryingComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Content.Server.Carrying
{
/// <summary>
/// Used so we can sub to events like VirtualItemDeleted
/// </summary>
[RegisterComponent]
public sealed class CarryingComponent : Component
{}
}
229 changes: 229 additions & 0 deletions Content.Server/2G2C/Carrying/CarryingSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
using System.Threading;
using Content.Server.DoAfter;
using Content.Server.Hands.Systems;
using Content.Server.Hands.Components;
using Content.Shared.MobState.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Hands;
using Content.Shared.Stunnable;
using Content.Shared.Interaction.Events;
using Content.Shared.Verbs;
using Content.Shared.Carrying;
using Content.Shared.Movement.Events;
using Content.Shared.Pulling;
using Content.Shared.Pulling.Components;
using Content.Shared.Standing;
using Content.Shared.ActionBlocker;
using Robust.Shared.Physics;

namespace Content.Server.Carrying
{
public sealed class CarryingSystem : EntitySystem
{
[Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly CarryingSlowdownSystem _slowdown = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly StandingStateSystem _standingState = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedPullingSystem _pullingSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CarriableComponent, GetVerbsEvent<AlternativeVerb>>(AddCarryVerb);
SubscribeLocalEvent<CarryingComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
SubscribeLocalEvent<BeingCarriedComponent, UpdateCanMoveEvent>(OnMoveAttempt);
SubscribeLocalEvent<BeingCarriedComponent, StandAttemptEvent>(OnStandAttempt);
SubscribeLocalEvent<BeingCarriedComponent, GettingInteractedWithAttemptEvent>(OnInteractedWith);
SubscribeLocalEvent<CarrySuccessfulEvent>(OnCarrySuccess);
SubscribeLocalEvent<CarryCancelledEvent>(OnCarryCancelled);
}


private void AddCarryVerb(EntityUid uid, CarriableComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;

if (!CanCarry(args.User, uid, component))
return;

if (HasComp<CarryingComponent>(args.User)) // yeah not dealing with that
return;

if (!HasComp<KnockedDownComponent>(uid) && !(TryComp<MobStateComponent>(uid, out var state) && (state.IsCritical() || state.IsDead() || state.IsIncapacitated())))
return;

AlternativeVerb verb = new()
{
Act = () =>
{
StartCarryDoAfter(args.User, uid, component);
},
Text = Loc.GetString("carry-verb"),
Priority = 2
};
args.Verbs.Add(verb);
}

private void OnVirtualItemDeleted(EntityUid uid, CarryingComponent component, VirtualItemDeletedEvent args)
{
if (!HasComp<CarriableComponent>(args.BlockingEntity))
return;

DropCarried(uid, args.BlockingEntity);
}

private void OnMoveAttempt(EntityUid uid, BeingCarriedComponent component, UpdateCanMoveEvent args)
{
args.Cancel();
}

private void OnStandAttempt(EntityUid uid, BeingCarriedComponent component, StandAttemptEvent args)
{
args.Cancel();
}

private void OnInteractedWith(EntityUid uid, BeingCarriedComponent component, GettingInteractedWithAttemptEvent args)
{
if (args.Uid != component.Carrier)
args.Cancel();
}

private void OnCarrySuccess(CarrySuccessfulEvent ev)
{
if (!CanCarry(ev.Carrier, ev.Carried, ev.Component))
return;

Carry(ev.Carrier, ev.Carried);
}

private void OnCarryCancelled(CarryCancelledEvent ev)
{
if (ev.Component == null)
return;

ev.Component.CancelToken = null;
}

private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component)
{
if (component.CancelToken != null)
{
component.CancelToken.Cancel();
component.CancelToken = null;
}

component.CancelToken = new CancellationTokenSource();
_doAfterSystem.DoAfter(new DoAfterEventArgs(carrier, 3f, component.CancelToken.Token, target: carried)
{
BroadcastFinishedEvent = new CarrySuccessfulEvent(carrier, carried, component),
BroadcastCancelledEvent = new CarryCancelledEvent(carrier, component),
BreakOnTargetMove = true,
BreakOnUserMove = true,
BreakOnStun = true,
NeedHand = true
});
}

private void Carry(EntityUid carrier, EntityUid carried)
{
if (TryComp<SharedPullableComponent>(carried, out var pullable))
_pullingSystem.TryStopPull(pullable);

Transform(carried).Coordinates = Transform(carrier).Coordinates;
Transform(carried).ParentUid = carrier;
_virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier);
_virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier);
EnsureComp<CarryingComponent>(carrier);
ApplyCarrySlowdown(carrier, carried);
var carriedComp = EnsureComp<BeingCarriedComponent>(carried);
carriedComp.Carrier = carrier;
_actionBlockerSystem.UpdateCanMove(carried);
}

public void DropCarried(EntityUid carrier, EntityUid carried)
{
RemComp<CarryingComponent>(carrier); // get rid of this first so we don't recusrively fire that event
RemComp<CarryingSlowdownComponent>(carrier);
RemComp<BeingCarriedComponent>(carried);
_actionBlockerSystem.UpdateCanMove(carried);
_virtualItemSystem.DeleteInHandsMatching(carrier, carried);
Transform(carried).AttachToGridOrMap();
_standingState.Stand(carried);
}

private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried)
{
if (!TryComp<FixturesComponent>(carrier, out var carrierFixtures))
return;
if (!TryComp<FixturesComponent>(carried, out var carriedFixtures))
return;
if (carrierFixtures.Fixtures.Count == 0 || carriedFixtures.Fixtures.Count == 0)
return;

float carrierMass = 0f;
float carriedMass = 0f;
foreach (var fixture in carrierFixtures.Fixtures.Values)
{
carrierMass += fixture.Mass;
}
foreach (var fixture in carriedFixtures.Fixtures.Values)
{
carriedMass += fixture.Mass;
}

if (carrierMass == 0f)
carrierMass = 70f;
if (carriedMass == 0f)
carriedMass = 70f;

var massRatioSq = Math.Pow((carriedMass / carrierMass), 2);
var modifier = (1 - (massRatioSq * 0.15));
var slowdownComp = EnsureComp<CarryingSlowdownComponent>(carrier);
_slowdown.SetModifier(carrier, (float) modifier, (float) modifier, slowdownComp);
}

public bool CanCarry(EntityUid carrier, EntityUid carried, CarriableComponent? carriedComp = null)
{
if (!Resolve(carried, ref carriedComp))
return false;

if (!TryComp<HandsComponent>(carrier, out var hands))
return false;

if (hands.CountFreeHands() < carriedComp.FreeHandsRequired)
return false;

return true;
}

private sealed class CarryCancelledEvent : EntityEventArgs
{
public EntityUid Uid;

public CarriableComponent Component;

public CarryCancelledEvent(EntityUid uid, CarriableComponent component)
{
Uid = uid;
Component = component;
}
}

private sealed class CarrySuccessfulEvent : EntityEventArgs
{
public EntityUid Carrier;

public EntityUid Carried;

public CarriableComponent Component;

public CarrySuccessfulEvent(EntityUid carrier, EntityUid carried, CarriableComponent component)
{
Carrier = carrier;
Carried = carried;
Component = component;
}
}
}
}
28 changes: 28 additions & 0 deletions Content.Shared/2G2C/Carrying/CarryingSlowdownComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;

namespace Content.Shared.Carrying
{
[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSlowdownSystem))]

public sealed class CarryingSlowdownComponent : Component
{
[DataField("walkModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)]
public float WalkModifier = 1.0f;

[DataField("sprintModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)]
public float SprintModifier = 1.0f;
}

[Serializable, NetSerializable]
public sealed class CarryingSlowdownComponentState : ComponentState
{
public float WalkModifier;
public float SprintModifier;
public CarryingSlowdownComponentState(float walkModifier, float sprintModifier)
{
WalkModifier = walkModifier;
SprintModifier = sprintModifier;
}
}
}
47 changes: 47 additions & 0 deletions Content.Shared/2G2C/Carrying/CarryingSlowdownSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Content.Shared.Movement.Systems;
using Robust.Shared.GameStates;

namespace Content.Shared.Carrying
{
public sealed class CarryingSlowdownSystem : EntitySystem
{
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CarryingSlowdownComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<CarryingSlowdownComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<CarryingSlowdownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMoveSpeed);
}

public void SetModifier(EntityUid uid, float walkSpeedModifier, float sprintSpeedModifier, CarryingSlowdownComponent? component = null)
{
if (!Resolve(uid, ref component))
return;

component.WalkModifier = walkSpeedModifier;
component.SprintModifier = sprintSpeedModifier;
_movementSpeed.RefreshMovementSpeedModifiers(uid);
}
private void OnGetState(EntityUid uid, CarryingSlowdownComponent component, ref ComponentGetState args)
{
args.State = new CarryingSlowdownComponentState(component.WalkModifier, component.SprintModifier);
}

private void OnHandleState(EntityUid uid, CarryingSlowdownComponent component, ref ComponentHandleState args)
{
if (args.Current is CarryingSlowdownComponentState state)
{
component.WalkModifier = state.WalkModifier;
component.SprintModifier = state.SprintModifier;

_movementSpeed.RefreshMovementSpeedModifiers(uid);
}
}
private void OnRefreshMoveSpeed(EntityUid uid, CarryingSlowdownComponent component, RefreshMovementSpeedModifiersEvent args)
{
args.ModifySpeed(component.WalkModifier, component.SprintModifier);
}
}
}