The real movement refactor (#9645)

* The real movement refactor

* ref events

* Jetpack cleanup

* a

* Vehicles partially working

* Balance tweaks

* Restore some shitcode

* AAAAAAAA

* Even more prediction

* ECS compstate trying to fix this

* yml

* vehicles kill me

* Don't lock keys

* a

* Fix problem

* Fix sounds

* shuttle inputs

* Shuttle controls

* space brakes

* Keybinds

* Fix merge

* Handle shutdown

* Fix keys

* Bump friction

* fix buckle offset

* Fix relay and friction

* Fix jetpack turning

* contexts amirite
This commit is contained in:
metalgearsloth
2022-07-16 13:51:52 +10:00
committed by GitHub
parent e0b7b48cae
commit b9e876ca92
109 changed files with 1752 additions and 1584 deletions

View File

@@ -41,12 +41,6 @@ namespace Content.Server.AI.Commands
return;
}
// TODO: IMover refffaaccctttooorrr
if (_entities.HasComponent<IMoverComponent>(entId))
{
_entities.RemoveComponent<IMoverComponent>(entId);
}
var comp = _entities.AddComponent<UtilityAi>(entId);
var behaviorManager = IoCManager.Resolve<INpcBehaviorManager>();

View File

@@ -123,7 +123,7 @@ namespace Content.Server.AI.Steering
/// <exception cref="InvalidOperationException"></exception>
public void Unregister(EntityUid entity)
{
if (EntityManager.TryGetComponent(entity, out SharedPlayerInputMoverComponent? controller))
if (EntityManager.TryGetComponent(entity, out InputMoverComponent? controller))
{
controller.CurTickSprintMovement = Vector2.Zero;
}
@@ -231,11 +231,11 @@ namespace Content.Server.AI.Steering
_listIndex = (_listIndex + 1) % _agentLists.Count;
}
private void SetDirection(SharedPlayerInputMoverComponent component, Vector2 value)
private void SetDirection(InputMoverComponent component, Vector2 value)
{
component.CurTickSprintMovement = value;
component._lastInputTick = _timing.CurTick;
component._lastInputSubTick = ushort.MaxValue;
component.LastInputTick = _timing.CurTick;
component.LastInputSubTick = ushort.MaxValue;
}
/// <summary>
@@ -250,7 +250,7 @@ namespace Content.Server.AI.Steering
{
// Main optimisation to be done below is the redundant calls and adding more variables
if (Deleted(entity) ||
!EntityManager.TryGetComponent(entity, out SharedPlayerInputMoverComponent? controller) ||
!EntityManager.TryGetComponent(entity, out InputMoverComponent? controller) ||
!controller.CanMove ||
!TryComp(entity, out TransformComponent? xform) ||
xform.GridUid == null)

View File

@@ -18,12 +18,12 @@ namespace Content.Server.Body.Systems
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BodyComponent, RelayMoveInputEvent>(OnRelayMoveInput);
SubscribeLocalEvent<BodyComponent, MoveInputEvent>(OnRelayMoveInput);
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
SubscribeLocalEvent<BodyComponent, BeingMicrowavedEvent>(OnBeingMicrowaved);
}
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, RelayMoveInputEvent args)
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
{
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) &&
mobState.IsDead() &&

View File

@@ -40,8 +40,7 @@ namespace Content.Server.Body.Systems
Comp<GhostOnMoveComponent>(newEntity).MustBeDead = true;
// TODO: This is an awful solution.
if (!EntityManager.HasComponent<IMoverComponent>(newEntity))
EntityManager.AddComponent<SharedDummyInputMoverComponent>(newEntity);
EnsureComp<InputMoverComponent>(newEntity);
oldMind.Mind?.TransferTo(newEntity);
}

View File

@@ -68,7 +68,7 @@ namespace Content.Server.Buckle.Components
_buckledTo = value;
_buckleTime = _gameTiming.CurTime;
_sysMan.GetEntitySystem<ActionBlockerSystem>().UpdateCanMove(Owner);
Dirty(_entMan);
Dirty(EntMan);
}
}
@@ -99,33 +99,6 @@ namespace Content.Server.Buckle.Components
}
}
/// <summary>
/// Reattaches this entity to the strap, modifying its position and rotation.
/// </summary>
/// <param name="strap">The strap to reattach to.</param>
public void ReAttach(StrapComponent strap)
{
var ownTransform = _entMan.GetComponent<TransformComponent>(Owner);
var strapTransform = _entMan.GetComponent<TransformComponent>(strap.Owner);
ownTransform.AttachParent(strapTransform);
ownTransform.LocalRotation = Angle.Zero;
switch (strap.Position)
{
case StrapPosition.None:
break;
case StrapPosition.Stand:
EntitySystem.Get<StandingStateSystem>().Stand(Owner);
break;
case StrapPosition.Down:
EntitySystem.Get<StandingStateSystem>().Down(Owner, false, false);
break;
}
ownTransform.LocalPosition = strap.BuckleOffset;
}
public bool CanBuckle(EntityUid user, EntityUid to, [NotNullWhen(true)] out StrapComponent? strap)
{
var popupSystem = EntitySystem.Get<SharedPopupSystem>();
@@ -136,7 +109,7 @@ namespace Content.Server.Buckle.Components
return false;
}
if (!_entMan.TryGetComponent(to, out strap))
if (!EntMan.TryGetComponent(to, out strap))
{
return false;
}
@@ -160,7 +133,7 @@ namespace Content.Server.Buckle.Components
}
}
if (!_entMan.HasComponent<HandsComponent>(user))
if (!EntMan.HasComponent<HandsComponent>(user))
{
popupSystem.PopupEntity(Loc.GetString("buckle-component-no-hands-message"), user, Filter.Entities(user));
return false;
@@ -176,10 +149,10 @@ namespace Content.Server.Buckle.Components
return false;
}
var parent = _entMan.GetComponent<TransformComponent>(to).Parent;
var parent = EntMan.GetComponent<TransformComponent>(to).Parent;
while (parent != null)
{
if (parent == _entMan.GetComponent<TransformComponent>(user))
if (parent == EntMan.GetComponent<TransformComponent>(user))
{
var message = Loc.GetString(Owner == user
? "buckle-component-cannot-buckle-message"
@@ -224,7 +197,7 @@ namespace Content.Server.Buckle.Components
return false;
}
if(_entMan.TryGetComponent<AppearanceComponent>(Owner, out var appearance))
if(EntMan.TryGetComponent<AppearanceComponent>(Owner, out var appearance))
appearance.SetData(BuckleVisuals.Buckled, true);
ReAttach(strap);
@@ -236,10 +209,10 @@ namespace Content.Server.Buckle.Components
UpdateBuckleStatus();
var ev = new BuckleChangeEvent() { Buckling = true, Strap = BuckledTo.Owner, BuckledEntity = Owner };
_entMan.EventBus.RaiseLocalEvent(ev.BuckledEntity, ev, false);
_entMan.EventBus.RaiseLocalEvent(ev.Strap, ev, false);
EntMan.EventBus.RaiseLocalEvent(ev.BuckledEntity, ev, false);
EntMan.EventBus.RaiseLocalEvent(ev.Strap, ev, false);
if (_entMan.TryGetComponent(Owner, out SharedPullableComponent? ownerPullable))
if (EntMan.TryGetComponent(Owner, out SharedPullableComponent? ownerPullable))
{
if (ownerPullable.Puller != null)
{
@@ -247,7 +220,7 @@ namespace Content.Server.Buckle.Components
}
}
if (_entMan.TryGetComponent(to, out SharedPullableComponent? toPullable))
if (EntMan.TryGetComponent(to, out SharedPullableComponent? toPullable))
{
if (toPullable.Puller == Owner)
{
@@ -292,7 +265,7 @@ namespace Content.Server.Buckle.Components
return false;
}
// If the strap is a vehicle and the rider is not the person unbuckling, return.
if (_entMan.TryGetComponent<VehicleComponent>(oldBuckledTo.Owner, out var vehicle) &&
if (EntMan.TryGetComponent<VehicleComponent>(oldBuckledTo.Owner, out var vehicle) &&
vehicle.Rider != user)
return false;
}
@@ -312,11 +285,11 @@ namespace Content.Server.Buckle.Components
xform.Coordinates = oldBuckledXform.Coordinates.Offset(oldBuckledTo.UnbuckleOffset);
}
if(_entMan.TryGetComponent<AppearanceComponent>(Owner, out var appearance))
if(EntMan.TryGetComponent<AppearanceComponent>(Owner, out var appearance))
appearance.SetData(BuckleVisuals.Buckled, false);
if (_entMan.HasComponent<KnockedDownComponent>(Owner)
| (_entMan.TryGetComponent<MobStateComponent>(Owner, out var mobState) && mobState.IsIncapacitated()))
if (EntMan.HasComponent<KnockedDownComponent>(Owner)
| (EntMan.TryGetComponent<MobStateComponent>(Owner, out var mobState) && mobState.IsIncapacitated()))
{
EntitySystem.Get<StandingStateSystem>().Down(Owner);
}
@@ -334,8 +307,8 @@ namespace Content.Server.Buckle.Components
SoundSystem.Play(oldBuckledTo.UnbuckleSound.GetSound(), Filter.Pvs(Owner), Owner);
var ev = new BuckleChangeEvent() { Buckling = false, Strap = oldBuckledTo.Owner, BuckledEntity = Owner };
_entMan.EventBus.RaiseLocalEvent(Owner, ev, false);
_entMan.EventBus.RaiseLocalEvent(oldBuckledTo.Owner, ev, false);
EntMan.EventBus.RaiseLocalEvent(Owner, ev, false);
EntMan.EventBus.RaiseLocalEvent(oldBuckledTo.Owner, ev, false);
return true;
}
@@ -386,8 +359,8 @@ namespace Content.Server.Buckle.Components
int? drawDepth = null;
if (BuckledTo != null &&
_entMan.GetComponent<TransformComponent>(BuckledTo.Owner).LocalRotation.GetCardinalDir() == Direction.North &&
_entMan.TryGetComponent<SpriteComponent>(BuckledTo.Owner, out var spriteComponent))
EntMan.GetComponent<TransformComponent>(BuckledTo.Owner).LocalRotation.GetCardinalDir() == Direction.North &&
EntMan.TryGetComponent<SpriteComponent>(BuckledTo.Owner, out var spriteComponent))
{
drawDepth = spriteComponent.DrawDepth - 1;
}

View File

@@ -10,12 +10,10 @@ namespace Content.Server.Buckle.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedStrapComponent))]
public sealed class StrapComponent : SharedStrapComponent, ISerializationHooks
public sealed class StrapComponent : SharedStrapComponent
{
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly HashSet<EntityUid> _buckledEntities = new();
/// <summary>
/// The angle in degrees to rotate the player by when they get strapped
/// </summary>
@@ -28,13 +26,6 @@ namespace Content.Server.Buckle.Components
[ViewVariables] [DataField("size")] private int _size = 100;
private int _occupiedSize;
/// <summary>
/// The buckled entity will be offset by this amount from the center of the strap object.
/// If this offset it too big, it will be clamped to <see cref="MaxBuckleDistance"/>
/// </summary>
[DataField("buckleOffset", required: false)]
public Vector2 BuckleOffsetUnclamped = Vector2.Zero;
private bool _enabled = true;
/// <summary>
@@ -51,38 +42,11 @@ namespace Content.Server.Buckle.Components
}
}
/// <summary>
/// The distance above which a buckled entity will be automatically unbuckled.
/// Don't change it unless you really have to
/// </summary>
[DataField("maxBuckleDistance", required: false)]
public float MaxBuckleDistance = 0.1f;
/// <summary>
/// You can specify the offset the entity will have after unbuckling.
/// </summary>
[DataField("unbuckleOffset", required: false)]
public Vector2 UnbuckleOffset = Vector2.Zero;
/// <summary>
/// Gets and clamps the buckle offset to MaxBuckleDistance
/// </summary>
public Vector2 BuckleOffset => Vector2.Clamp(
BuckleOffsetUnclamped,
Vector2.One * -MaxBuckleDistance,
Vector2.One * MaxBuckleDistance);
/// <summary>
/// The entity that is currently buckled here, synced from <see cref="BuckleComponent.BuckledTo"/>
/// </summary>
public IReadOnlyCollection<EntityUid> BuckledEntities => _buckledEntities;
/// <summary>
/// The change in position to the strapped mob
/// </summary>
[DataField("position")]
public StrapPosition Position { get; } = StrapPosition.None;
/// <summary>
/// The sound to be played when a mob is buckled
/// </summary>
@@ -138,7 +102,7 @@ namespace Content.Server.Buckle.Components
return false;
}
if (!_buckledEntities.Add(buckle.Owner))
if (!BuckledEntities.Add(buckle.Owner))
{
return false;
}
@@ -154,6 +118,7 @@ namespace Content.Server.Buckle.Components
appearance.SetData("StrapState", true);
}
Dirty();
return true;
}
@@ -164,7 +129,7 @@ namespace Content.Server.Buckle.Components
/// <param name="buckle">The component to remove</param>
public void Remove(BuckleComponent buckle)
{
if (_buckledEntities.Remove(buckle.Owner))
if (BuckledEntities.Remove(buckle.Owner))
{
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<AppearanceComponent>(Owner, out var appearance))
{
@@ -172,6 +137,7 @@ namespace Content.Server.Buckle.Components
}
_occupiedSize -= buckle.Size;
Dirty();
}
}
@@ -186,7 +152,7 @@ namespace Content.Server.Buckle.Components
{
var entManager = IoCManager.Resolve<IEntityManager>();
foreach (var entity in _buckledEntities.ToArray())
foreach (var entity in BuckledEntities.ToArray())
{
if (entManager.TryGetComponent<BuckleComponent>(entity, out var buckle))
{
@@ -194,13 +160,9 @@ namespace Content.Server.Buckle.Components
}
}
_buckledEntities.Clear();
BuckledEntities.Clear();
_occupiedSize = 0;
}
public override ComponentState GetComponentState()
{
return new StrapComponentState(Position);
Dirty();
}
public override bool DragDropOn(DragDropEvent eventArgs)

View File

@@ -2,11 +2,13 @@ using Content.Server.Buckle.Components;
using Content.Server.Interaction;
using Content.Server.Storage.Components;
using Content.Shared.Buckle;
using Content.Shared.Buckle.Components;
using Content.Shared.Interaction;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
namespace Content.Server.Buckle.Systems
{
@@ -20,20 +22,21 @@ namespace Content.Server.Buckle.Systems
UpdatesAfter.Add(typeof(InteractionSystem));
UpdatesAfter.Add(typeof(InputSystem));
SubscribeLocalEvent<BuckleComponent, MoveEvent>(MoveEvent);
SubscribeLocalEvent<StrapComponent, RotateEvent>(RotateEvent);
SubscribeLocalEvent<StrapComponent, ComponentGetState>(OnStrapGetState);
SubscribeLocalEvent<StrapComponent, EntInsertedIntoContainerMessage>(ContainerModifiedStrap);
SubscribeLocalEvent<StrapComponent, EntRemovedFromContainerMessage>(ContainerModifiedStrap);
SubscribeLocalEvent<BuckleComponent, MoveEvent>(MoveEvent);
SubscribeLocalEvent<BuckleComponent, InteractHandEvent>(HandleInteractHand);
SubscribeLocalEvent<BuckleComponent, GetVerbsEvent<InteractionVerb>>(AddUnbuckleVerb);
SubscribeLocalEvent<BuckleComponent, InsertIntoEntityStorageAttemptEvent>(OnEntityStorageInsertAttempt);
}
private void OnStrapGetState(EntityUid uid, StrapComponent component, ref ComponentGetState args)
{
args.State = new StrapComponentState(component.Position, component.BuckleOffset, component.BuckledEntities, component.MaxBuckleDistance);
}
private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsEvent<InteractionVerb> args)
{
if (!args.CanAccess || !args.CanInteract || !component.Buckled)
@@ -80,21 +83,6 @@ namespace Content.Server.Buckle.Systems
buckle.TryUnbuckle(buckle.Owner, true);
}
private void RotateEvent(EntityUid uid, StrapComponent strap, ref RotateEvent ev)
{
// On rotation of a strap, reattach all buckled entities.
// This fixes buckle offsets and draw depths.
foreach (var buckledEntity in strap.BuckledEntities)
{
if (!EntityManager.TryGetComponent(buckledEntity, out BuckleComponent? buckled))
{
continue;
}
buckled.ReAttach(strap);
Dirty(buckled);
}
}
private void ContainerModifiedStrap(EntityUid uid, StrapComponent strap, ContainerModifiedMessage message)
{
foreach (var buckledEntity in strap.BuckledEntities)

View File

@@ -24,7 +24,7 @@ namespace Content.Server.Disposal.Tube
base.Initialize();
SubscribeLocalEvent<DisposalTubeComponent, PhysicsBodyTypeChangedEvent>(BodyTypeChanged);
SubscribeLocalEvent<DisposalTubeComponent, RelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<DisposalTubeComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<DisposalTubeComponent, BreakageEventArgs>(OnBreak);
SubscribeLocalEvent<DisposalTaggerComponent, GetVerbsEvent<InteractionVerb>>(AddOpenUIVerbs);
SubscribeLocalEvent<DisposalRouterComponent, GetVerbsEvent<InteractionVerb>>(AddOpenUIVerbs);
@@ -65,7 +65,7 @@ namespace Content.Server.Disposal.Tube
args.Verbs.Add(verb);
}
private void OnRelayMovement(EntityUid uid, DisposalTubeComponent component, RelayMovementEntityEvent args)
private void OnRelayMovement(EntityUid uid, DisposalTubeComponent component, ref ContainerRelayMovementEntityEvent args)
{
if (_gameTiming.CurTime < component.LastClang + DisposalTubeComponent.ClangDelay)
{

View File

@@ -53,7 +53,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
// Shouldn't need re-anchoring.
SubscribeLocalEvent<DisposalUnitComponent, AnchorStateChangedEvent>(OnAnchorChanged);
// TODO: Predict me when hands predicted
SubscribeLocalEvent<DisposalUnitComponent, RelayMovementEntityEvent>(HandleMovement);
SubscribeLocalEvent<DisposalUnitComponent, ContainerRelayMovementEntityEvent>(HandleMovement);
SubscribeLocalEvent<DisposalUnitComponent, PowerChangedEvent>(HandlePowerChange);
// Component lifetime
@@ -376,7 +376,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
}
}
private void HandleMovement(EntityUid uid, DisposalUnitComponent component, RelayMovementEntityEvent args)
private void HandleMovement(EntityUid uid, DisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args)
{
var currentTime = GameTiming.CurTime;

View File

@@ -44,7 +44,7 @@ namespace Content.Server.Ghost
SubscribeLocalEvent<GhostComponent, MindRemovedMessage>(OnMindRemovedMessage);
SubscribeLocalEvent<GhostComponent, MindUnvisitedMessage>(OnMindUnvisitedMessage);
SubscribeLocalEvent<GhostOnMoveComponent, RelayMoveInputEvent>(OnRelayMoveInput);
SubscribeLocalEvent<GhostOnMoveComponent, MoveInputEvent>(OnRelayMoveInput);
SubscribeNetworkEvent<GhostWarpsRequestEvent>(OnGhostWarpsRequest);
SubscribeNetworkEvent<GhostReturnToBodyRequest>(OnGhostReturnToBodyRequest);
@@ -77,7 +77,7 @@ namespace Content.Server.Ghost
args.Handled = true;
}
private void OnRelayMoveInput(EntityUid uid, GhostOnMoveComponent component, RelayMoveInputEvent args)
private void OnRelayMoveInput(EntityUid uid, GhostOnMoveComponent component, ref MoveInputEvent args)
{
// Let's not ghost if our mind is visiting...
if (EntityManager.HasComponent<VisitingMindComponent>(uid)) return;

View File

@@ -75,7 +75,7 @@ namespace Content.Server.Medical.CrewMonitoring
// the monitor. But in the special case where the monitor IS a player (i.e., admin ghost), we base it off
// the players eye rotation. We don't know what that is for sure, but we know their last grid angle, which
// should work well enough?
if (TryComp(uid, out IMoverComponent? mover))
if (TryComp(uid, out InputMoverComponent? mover))
worldRot = mover.LastGridAngle;
else if (_mapManager.TryGetGrid(xform.GridUid, out var grid))
worldRot = grid.WorldRotation;

View File

@@ -41,7 +41,7 @@ namespace Content.Server.Medical
SubscribeLocalEvent<MedicalScannerComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<MedicalScannerComponent, ActivateInWorldEvent>(OnActivated);
SubscribeLocalEvent<MedicalScannerComponent, RelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<MedicalScannerComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<MedicalScannerComponent, GetVerbsEvent<InteractionVerb>>(AddInsertOtherVerb);
SubscribeLocalEvent<MedicalScannerComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<MedicalScannerComponent, DestructionEventArgs>(OnDestroyed);
@@ -65,7 +65,7 @@ namespace Content.Server.Medical
UpdateUserInterface(uid, scannerComponent);
}
private void OnRelayMovement(EntityUid uid, MedicalScannerComponent scannerComponent, RelayMovementEntityEvent args)
private void OnRelayMovement(EntityUid uid, MedicalScannerComponent scannerComponent, ref ContainerRelayMovementEntityEvent args)
{
if (!_blocker.CanInteract(args.Entity, scannerComponent.Owner))
return;

View File

@@ -44,13 +44,12 @@ namespace Content.Server.Mind.Commands
public static void MakeSentient(EntityUid uid, IEntityManager entityManager)
{
if(entityManager.HasComponent<AiControllerComponent>(uid))
entityManager.RemoveComponent<AiControllerComponent>(uid);
entityManager.RemoveComponent<AiControllerComponent>(uid);
entityManager.EnsureComponent<MindComponent>(uid);
entityManager.EnsureComponent<SharedPlayerInputMoverComponent>(uid);
entityManager.EnsureComponent<SharedPlayerMobMoverComponent>(uid);
entityManager.EnsureComponent<InputMoverComponent>(uid);
entityManager.EnsureComponent<MobMoverComponent>(uid);
entityManager.EnsureComponent<MovementSpeedModifierComponent>(uid);
entityManager.EnsureComponent<SharedSpeechComponent>(uid);
entityManager.EnsureComponent<SharedEmotingComponent>(uid);
entityManager.EnsureComponent<ExaminerComponent>(uid);

View File

@@ -1,15 +1,12 @@
using Content.Server.Cargo.Components;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Shared.Vehicle.Components;
using Content.Shared.Movement;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Content.Server.Physics.Controllers
{
@@ -18,14 +15,8 @@ namespace Content.Server.Physics.Controllers
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly ThrusterSystem _thruster = default!;
/// <summary>
/// These mobs will get skipped over when checking which mobs
/// should be moved. Prediction is handled elsewhere by
/// cancelling the movement attempt in the shared and
/// client namespace.
/// </summary>
private HashSet<EntityUid> _excludedMobs = new();
private Dictionary<ShuttleComponent, List<(PilotComponent, IMoverComponent, TransformComponent)>> _shuttlePilots = new();
private Dictionary<ShuttleComponent, List<(PilotComponent, InputMoverComponent, TransformComponent)>> _shuttlePilots = new();
protected override Filter GetSoundPlayers(EntityUid mover)
{
@@ -40,31 +31,166 @@ namespace Content.Server.Physics.Controllers
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
_excludedMobs.Clear();
foreach (var (mobMover, mover, physics, xform) in EntityManager.EntityQuery<IMobMoverComponent, IMoverComponent, PhysicsComponent, TransformComponent>())
var bodyQuery = GetEntityQuery<PhysicsComponent>();
var relayQuery = GetEntityQuery<RelayInputMoverComponent>();
foreach (var (mover, xform) in EntityQuery<InputMoverComponent, TransformComponent>())
{
_excludedMobs.Add(mover.Owner);
HandleMobMovement(mover, physics, mobMover, xform, frameTime);
if (relayQuery.TryGetComponent(mover.Owner, out var relayed) && relayed != null)
{
continue;
}
PhysicsComponent? body = null;
if (mover.ToParent && relayQuery.HasComponent(xform.ParentUid))
{
if (!bodyQuery.TryGetComponent(xform.ParentUid, out body)) continue;
}
else if (!bodyQuery.TryGetComponent(mover.Owner, out body))
{
continue;
}
HandleMobMovement(mover, body, xform, frameTime);
}
HandleShuttleMovement(frameTime);
HandleVehicleMovement();
}
foreach (var (mover, physics) in EntityManager.EntityQuery<IMoverComponent, PhysicsComponent>(true))
public (Vector2 Strafe, float Rotation, float Brakes) GetPilotVelocityInput(PilotComponent component)
{
if (!Timing.InSimulation)
{
if (_excludedMobs.Contains(mover.Owner)) continue;
HandleKinematicMovement(mover, physics);
// Outside of simulation we'll be running client predicted movement per-frame.
// So return a full-length vector as if it's a full tick.
// Physics system will have the correct time step anyways.
ResetSubtick(component);
ApplyTick(component, 1f);
return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking);
}
float remainingFraction;
if (Timing.CurTick > component.LastInputTick)
{
component.CurTickStrafeMovement = Vector2.Zero;
component.CurTickRotationMovement = 0f;
component.CurTickBraking = 0f;
remainingFraction = 1;
}
else
{
remainingFraction = (ushort.MaxValue - component.LastInputSubTick) / (float) ushort.MaxValue;
}
ApplyTick(component, remainingFraction);
// Logger.Info($"{curDir}{walk}{sprint}");
return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking);
}
private void ResetSubtick(PilotComponent component)
{
if (Timing.CurTick <= component.LastInputTick) return;
component.CurTickStrafeMovement = Vector2.Zero;
component.CurTickRotationMovement = 0f;
component.CurTickBraking = 0f;
component.LastInputTick = Timing.CurTick;
component.LastInputSubTick = 0;
}
protected override void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state)
{
if (!TryComp<PilotComponent>(uid, out var pilot) || pilot.Console == null) return;
ResetSubtick(pilot);
if (subTick >= pilot.LastInputSubTick)
{
var fraction = (subTick - pilot.LastInputSubTick) / (float) ushort.MaxValue;
ApplyTick(pilot, fraction);
pilot.LastInputSubTick = subTick;
}
var buttons = pilot.HeldButtons;
if (state)
{
buttons |= button;
}
else
{
buttons &= ~button;
}
pilot.HeldButtons = buttons;
}
private void ApplyTick(PilotComponent component, float fraction)
{
var x = 0;
var y = 0;
var rot = 0;
int brake;
if ((component.HeldButtons & ShuttleButtons.StrafeLeft) != 0x0)
{
x -= 1;
}
if ((component.HeldButtons & ShuttleButtons.StrafeRight) != 0x0)
{
x += 1;
}
component.CurTickStrafeMovement.X += x * fraction;
if ((component.HeldButtons & ShuttleButtons.StrafeUp) != 0x0)
{
y += 1;
}
if ((component.HeldButtons & ShuttleButtons.StrafeDown) != 0x0)
{
y -= 1;
}
component.CurTickStrafeMovement.Y += y * fraction;
if ((component.HeldButtons & ShuttleButtons.RotateLeft) != 0x0)
{
rot -= 1;
}
if ((component.HeldButtons & ShuttleButtons.RotateRight) != 0x0)
{
rot += 1;
}
component.CurTickRotationMovement += rot * fraction;
if ((component.HeldButtons & ShuttleButtons.Brake) != 0x0)
{
brake = 1;
}
else
{
brake = 0;
}
component.CurTickBraking += brake * fraction;
}
private void HandleShuttleMovement(float frameTime)
{
var newPilots = new Dictionary<ShuttleComponent, List<(PilotComponent Pilot, IMoverComponent Mover, TransformComponent ConsoleXform)>>();
var newPilots = new Dictionary<ShuttleComponent, List<(PilotComponent Pilot, InputMoverComponent Mover, TransformComponent ConsoleXform)>>();
// We just mark off their movement and the shuttle itself does its own movement
foreach (var (pilot, mover) in EntityManager.EntityQuery<PilotComponent, SharedPlayerInputMoverComponent>())
foreach (var (pilot, mover) in EntityManager.EntityQuery<PilotComponent, InputMoverComponent>())
{
var consoleEnt = pilot.Console?.Owner;
@@ -76,8 +202,6 @@ namespace Content.Server.Physics.Controllers
if (!TryComp<TransformComponent>(consoleEnt, out var xform)) continue;
_excludedMobs.Add(mover.Owner);
var gridId = xform.GridUid;
// This tries to see if the grid is a shuttle and if the console should work.
if (!_mapManager.TryGetGrid(gridId, out var grid) ||
@@ -86,7 +210,7 @@ namespace Content.Server.Physics.Controllers
if (!newPilots.TryGetValue(shuttleComponent, out var pilots))
{
pilots = new List<(PilotComponent, IMoverComponent, TransformComponent)>();
pilots = new List<(PilotComponent, InputMoverComponent, TransformComponent)>();
newPilots[shuttleComponent] = pilots;
}
@@ -113,37 +237,57 @@ namespace Content.Server.Physics.Controllers
var linearInput = Vector2.Zero;
var angularInput = 0f;
switch (shuttle.Mode)
foreach (var (pilot, _, consoleXform) in pilots)
{
case ShuttleMode.Cruise:
foreach (var (pilot, mover, consoleXform) in pilots)
var pilotInput = GetPilotVelocityInput(pilot);
// On the one hand we could just make it relay inputs to brake
// but uhh may be disorienting? n
if (pilotInput.Brakes > 0f)
{
if (body.LinearVelocity.Length > 0f)
{
var sprint = mover.VelocityDir.sprinting;
var force = body.LinearVelocity.Normalized * pilotInput.Brakes / body.InvMass * 3f;
var impulse = force * body.InvMass * frameTime;
if (sprint.Equals(Vector2.Zero)) continue;
var offsetRotation = consoleXform.LocalRotation;
linearInput += offsetRotation.RotateVec(new Vector2(0f, sprint.Y));
angularInput += sprint.X;
if (impulse.Length > body.LinearVelocity.Length)
{
body.LinearVelocity = Vector2.Zero;
}
else
{
body.ApplyLinearImpulse(-force * frameTime);
}
}
break;
case ShuttleMode.Strafing:
// No angular input possible
foreach (var (pilot, mover, consoleXform) in pilots)
if (body.AngularVelocity != 0f)
{
var sprint = mover.VelocityDir.sprinting;
var force = body.AngularVelocity * pilotInput.Brakes / body.InvI * 2f;
var impulse = force * body.InvI * frameTime;
if (sprint.Equals(Vector2.Zero)) continue;
var offsetRotation = consoleXform.LocalRotation;
sprint = offsetRotation.RotateVec(sprint);
linearInput += sprint;
if (MathF.Abs(impulse) > MathF.Abs(body.AngularVelocity))
{
body.AngularVelocity = 0f;
}
else
{
body.ApplyAngularImpulse(-force * frameTime);
}
}
break;
default:
throw new ArgumentOutOfRangeException();
continue;
}
if (pilotInput.Strafe.Length > 0f)
{
var offsetRotation = consoleXform.LocalRotation;
linearInput += offsetRotation.RotateVec(pilotInput.Strafe);
}
if (pilotInput.Rotation != 0f)
{
angularInput += pilotInput.Rotation;
}
}
var count = pilots.Count;
@@ -224,8 +368,7 @@ namespace Content.Server.Physics.Controllers
totalForce += force;
}
var dragForce = body.LinearVelocity * (totalForce.Length / _shuttle.ShuttleMaxLinearSpeed);
body.ApplyLinearImpulse((totalForce - dragForce) * frameTime);
body.ApplyLinearImpulse(totalForce * frameTime);
}
if (MathHelper.CloseTo(angularInput, 0f))
@@ -263,26 +406,5 @@ namespace Content.Server.Physics.Controllers
(ftl.State & (FTLState.Starting | FTLState.Travelling | FTLState.Arriving)) != 0x0);
}
/// <summary>
/// Add mobs riding vehicles to the list of mobs whose input
/// should be ignored.
/// </summary>
private void HandleVehicleMovement()
{
// TODO: Nuke this code. It's on my list.
foreach (var (rider, mover) in EntityQuery<RiderComponent, SharedPlayerInputMoverComponent>())
{
if (rider.Vehicle == null) continue;
_excludedMobs.Add(mover.Owner);
if (!_excludedMobs.Add(rider.Vehicle.Owner)) continue;
if (!TryComp<IMoverComponent>(rider.Vehicle.Owner, out var vehicleMover) ||
!TryComp<PhysicsComponent>(rider.Vehicle.Owner, out var vehicleBody) ||
rider.Vehicle.Owner.IsWeightless(vehicleBody, mapManager: _mapManager, entityManager: EntityManager)) continue;
HandleKinematicMovement(vehicleMover, vehicleBody);
}
}
}
}

View File

@@ -22,13 +22,13 @@ public sealed class EscapeInventorySystem : EntitySystem
{
base.Initialize();
SubscribeLocalEvent<CanEscapeInventoryComponent, RelayMoveInputEvent>(OnRelayMovement);
SubscribeLocalEvent<CanEscapeInventoryComponent, MoveInputEvent>(OnRelayMovement);
SubscribeLocalEvent<CanEscapeInventoryComponent, UpdateCanMoveEvent>(OnMoveAttempt);
SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeDoAfterComplete>(OnEscapeComplete);
SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeDoAfterCancel>(OnEscapeFail);
}
private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent component, RelayMoveInputEvent args)
private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent component, ref MoveInputEvent args)
{
//Prevents the user from creating multiple DoAfters if they're already resisting.
if (component.IsResisting == true)

View File

@@ -21,13 +21,13 @@ public sealed class ResistLockerSystem : EntitySystem
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ResistLockerComponent, RelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<ResistLockerComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<ResistLockerComponent, ResistDoAfterComplete>(OnDoAfterComplete);
SubscribeLocalEvent<ResistLockerComponent, ResistDoAfterCancelled>(OnDoAfterCancelled);
SubscribeLocalEvent<ResistLockerComponent, EntRemovedFromContainerMessage>(OnRemovedFromContainer);
}
private void OnRelayMovement(EntityUid uid, ResistLockerComponent component, RelayMovementEntityEvent args)
private void OnRelayMovement(EntityUid uid, ResistLockerComponent component, ref ContainerRelayMovementEntityEvent args)
{
if (component.IsResisting)
return;

View File

@@ -8,9 +8,6 @@ namespace Content.Server.Shuttles.Components
[ViewVariables]
public bool Enabled = true;
[ViewVariables]
public ShuttleMode Mode = ShuttleMode.Cruise;
/// <summary>
/// The cached thrust available for each cardinal direction
/// </summary>

View File

@@ -1,4 +1,3 @@
using Content.Server.Cargo.Components;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Shuttles.Components;
@@ -38,7 +37,6 @@ namespace Content.Server.Shuttles.Systems
SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(OnConsolePowerChange);
SubscribeLocalEvent<ShuttleConsoleComponent, AnchorStateChangedEvent>(OnConsoleAnchorChange);
SubscribeLocalEvent<ShuttleConsoleComponent, ActivatableUIOpenAttemptEvent>(OnConsoleUIOpenAttempt);
SubscribeLocalEvent<ShuttleConsoleComponent, ShuttleModeRequestMessage>(OnModeRequest);
SubscribeLocalEvent<ShuttleConsoleComponent, ShuttleConsoleDestinationMessage>(OnDestinationMessage);
SubscribeLocalEvent<ShuttleConsoleComponent, BoundUIClosedEvent>(OnConsoleUIClose);
@@ -47,6 +45,13 @@ namespace Content.Server.Shuttles.Systems
SubscribeLocalEvent<PilotComponent, MoveEvent>(HandlePilotMove);
SubscribeLocalEvent<PilotComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<PilotComponent, ComponentGetStateAttemptEvent>(OnGetStateAttempt);
}
private void OnGetStateAttempt(EntityUid uid, PilotComponent component, ref ComponentGetStateAttemptEvent args)
{
if (args.Cancelled || !TryComp<ActorComponent>(uid, out var actor) || actor.PlayerSession != args.Player)
args.Cancelled = true;
}
private void OnDestinationMessage(EntityUid uid, ShuttleConsoleComponent component, ShuttleConsoleDestinationMessage args)
@@ -184,64 +189,6 @@ namespace Content.Server.Shuttles.Systems
args.State = new PilotComponentState(component.Console?.Owner);
}
/// <summary>
/// Client is requesting a change in the shuttle's driving mode.
/// </summary>
private void OnModeRequest(EntityUid uid, ShuttleConsoleComponent component, ShuttleModeRequestMessage args)
{
var consoleUid = uid;
if (TryComp<CargoPilotConsoleComponent>(uid, out var cargoPilot) && cargoPilot.Entity != null)
{
consoleUid = cargoPilot.Entity.Value;
}
if (args.Session.AttachedEntity is not { } player ||
!TryComp<PilotComponent>(player, out var pilot) ||
!TryComp<ShuttleConsoleComponent>(consoleUid, out var console) ||
!TryComp<TransformComponent>(consoleUid, out var consoleXform)) return;
// Can't check console pilots as it may be remotely piloted!
if (!component.SubscribedPilots.Contains(pilot) ||
!TryComp<ShuttleComponent>(consoleXform.GridUid, out var shuttle)) return;
SetShuttleMode(args.Mode, console, shuttle);
UpdateState(component);
if (uid != consoleUid)
{
UpdateState(console);
}
}
/// <summary>
/// Sets the shuttle's movement mode. Does minimal revalidation.
/// </summary>
private void SetShuttleMode(ShuttleMode mode, ShuttleConsoleComponent consoleComponent,
ShuttleComponent shuttleComponent, TransformComponent? consoleXform = null)
{
// Re-validate
if (!this.IsPowered(consoleComponent.Owner, EntityManager) ||
!Resolve(consoleComponent.Owner, ref consoleXform) ||
!consoleXform.Anchored ||
consoleXform.GridUid != Transform(shuttleComponent.Owner).GridUid)
{
return;
}
shuttleComponent.Mode = mode;
switch (mode)
{
case ShuttleMode.Strafing:
break;
case ShuttleMode.Cruise:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Returns the position and angle of all dockingcomponents.
/// </summary>
@@ -286,7 +233,6 @@ namespace Content.Server.Shuttles.Systems
var range = radar?.MaxRange ?? 0f;
TryComp<ShuttleComponent>(consoleXform?.GridUid, out var shuttle);
var mode = shuttle?.Mode ?? ShuttleMode.Cruise;
var destinations = new List<(EntityUid, string, bool)>();
var ftlState = FTLState.Available;
@@ -342,7 +288,6 @@ namespace Content.Server.Shuttles.Systems
?.SetState(new ShuttleConsoleBoundInterfaceState(
ftlState,
ftlTime,
mode,
destinations,
range,
consoleXform?.Coordinates,

View File

@@ -67,7 +67,7 @@ namespace Content.Server.Storage.EntitySystems
SubscribeLocalEvent<ServerStorageComponent, EntRemovedFromContainerMessage>(OnStorageItemRemoved);
SubscribeLocalEvent<EntityStorageComponent, GetVerbsEvent<InteractionVerb>>(AddToggleOpenVerb);
SubscribeLocalEvent<EntityStorageComponent, RelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<EntityStorageComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
}
@@ -84,7 +84,7 @@ namespace Content.Server.Storage.EntitySystems
UpdateStorageUI(uid, storageComp);
}
private void OnRelayMovement(EntityUid uid, EntityStorageComponent component, RelayMovementEntityEvent args)
private void OnRelayMovement(EntityUid uid, EntityStorageComponent component, ref ContainerRelayMovementEntityEvent args)
{
if (!EntityManager.HasComponent<HandsComponent>(args.Entity))
return;

View File

@@ -1,57 +0,0 @@
using Content.Shared.Vehicle.Components;
using Content.Shared.Vehicle;
using Content.Shared.Toggleable;
using Content.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Audio;
namespace Content.Server.Vehicle
{
/// <summary>
/// Controls all the vehicle horns.
/// </summary>
public sealed class HonkSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<VehicleComponent, HonkActionEvent>(OnHonk);
SubscribeLocalEvent<VehicleComponent, ToggleActionEvent>(OnSirenToggle);
}
/// <summary>
/// This fires when the rider presses the honk action
/// </summary>
private void OnHonk(EntityUid uid, VehicleComponent vehicle, HonkActionEvent args)
{
if (args.Handled)
return;
if (vehicle.HornSound != null)
{
SoundSystem.Play(vehicle.HornSound.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.1f).WithVolume(8f));
args.Handled = true;
}
}
/// <summary>
/// For vehicles with horn sirens (like the secway) this uses different logic that makes the siren
/// loop instead of using a normal honk.
/// </summary>
private void OnSirenToggle(EntityUid uid, VehicleComponent vehicle, ToggleActionEvent args)
{
if (args.Handled || !vehicle.HornIsLooping)
return;
if (!vehicle.LoopingHornIsPlaying)
{
vehicle.SirenPlayingStream?.Stop();
vehicle.LoopingHornIsPlaying = true;
if (vehicle.HornSound != null)
vehicle.SirenPlayingStream = SoundSystem.Play(vehicle.HornSound.GetSound(), Filter.Pvs(uid), uid, AudioParams.Default.WithLoop(true).WithVolume(1.8f));
return;
}
vehicle.SirenPlayingStream?.Stop();
vehicle.LoopingHornIsPlaying = false;
}
}
}

View File

@@ -1,57 +0,0 @@
using Content.Server.Buckle.Components;
using Content.Shared.Vehicle.Components;
using Content.Shared.MobState;
using Content.Server.Standing;
using Content.Shared.Hands;
namespace Content.Server.Vehicle
{
public sealed class RiderSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RiderComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
SubscribeLocalEvent<RiderComponent, FellDownEvent>(OnFallDown);
SubscribeLocalEvent<RiderComponent, MobStateChangedEvent>(OnMobStateChanged);
}
/// <summary>
/// Kick the rider off the vehicle if they press q / drop the virtual item
/// </summary>
private void OnVirtualItemDeleted(EntityUid uid, RiderComponent component, VirtualItemDeletedEvent args)
{
if (args.BlockingEntity == component.Vehicle?.Owner)
{
UnbuckleFromVehicle(uid);
}
}
/// <summary>
/// Kick the rider off the vehicle if they get stunned
/// </summary>
private void OnFallDown(EntityUid uid, RiderComponent rider, FellDownEvent args)
{
UnbuckleFromVehicle(uid);
}
/// <summary>
/// Kick the rider off the vehicle if they go into crit or die.
/// </summary>
private void OnMobStateChanged(EntityUid uid, RiderComponent rider, MobStateChangedEvent args)
{
if (args.Component.IsCritical() || args.Component.IsDead())
{
UnbuckleFromVehicle(uid);
}
}
public void UnbuckleFromVehicle(EntityUid uid)
{
if (!TryComp<BuckleComponent>(uid, out var buckle))
return;
buckle.TryUnbuckle(uid, true);
}
}
}

View File

@@ -0,0 +1,92 @@
using Content.Server.Buckle.Components;
using Content.Shared.Vehicle.Components;
using Content.Shared.MobState;
using Content.Server.Standing;
using Content.Shared.Hands;
using Robust.Shared.GameStates;
namespace Content.Server.Vehicle
{
public sealed partial class VehicleSystem
{
private void InitializeRider()
{
SubscribeLocalEvent<RiderComponent, ComponentStartup>(OnRiderStartup);
SubscribeLocalEvent<RiderComponent, ComponentShutdown>(OnRiderShutdown);
SubscribeLocalEvent<RiderComponent, MetaFlagRemoveAttemptEvent>(OnRiderRemoval);
SubscribeLocalEvent<RiderComponent, ComponentGetState>(OnRiderGetState);
SubscribeLocalEvent<RiderComponent, ComponentGetStateAttemptEvent>(OnRiderGetStateAttempt);
SubscribeLocalEvent<RiderComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
SubscribeLocalEvent<RiderComponent, FellDownEvent>(OnFallDown);
SubscribeLocalEvent<RiderComponent, MobStateChangedEvent>(OnMobStateChanged);
}
private void OnRiderRemoval(EntityUid uid, RiderComponent component, ref MetaFlagRemoveAttemptEvent args)
{
if ((args.ToRemove & MetaDataFlags.EntitySpecific) != 0x0)
args.ToRemove = MetaDataFlags.None;
}
private void OnRiderStartup(EntityUid uid, RiderComponent component, ComponentStartup args)
{
_metadata.AddFlag(uid, MetaDataFlags.EntitySpecific);
}
private void OnRiderShutdown(EntityUid uid, RiderComponent component, ComponentShutdown args)
{
_metadata.RemoveFlag(uid, MetaDataFlags.EntitySpecific);
}
private void OnRiderGetStateAttempt(EntityUid uid, RiderComponent component, ref ComponentGetStateAttemptEvent args)
{
if (uid != args.Player.AttachedEntity)
args.Cancelled = true;
}
private void OnRiderGetState(EntityUid uid, RiderComponent component, ref ComponentGetState args)
{
args.State = new RiderComponentState()
{
Entity = component.Vehicle,
};
}
/// <summary>
/// Kick the rider off the vehicle if they press q / drop the virtual item
/// </summary>
private void OnVirtualItemDeleted(EntityUid uid, RiderComponent component, VirtualItemDeletedEvent args)
{
if (args.BlockingEntity == component.Vehicle)
{
UnbuckleFromVehicle(uid);
}
}
/// <summary>
/// Kick the rider off the vehicle if they get stunned
/// </summary>
private void OnFallDown(EntityUid uid, RiderComponent rider, FellDownEvent args)
{
UnbuckleFromVehicle(uid);
}
/// <summary>
/// Kick the rider off the vehicle if they go into crit or die.
/// </summary>
private void OnMobStateChanged(EntityUid uid, RiderComponent rider, MobStateChangedEvent args)
{
if (args.Component.IsCritical() || args.Component.IsDead())
{
UnbuckleFromVehicle(uid);
}
}
public void UnbuckleFromVehicle(EntityUid uid)
{
if (!TryComp<BuckleComponent>(uid, out var buckle))
return;
buckle.TryUnbuckle(uid, true);
}
}
}

View File

@@ -1,3 +1,4 @@
using Content.Server.Buckle.Components;
using Content.Shared.Vehicle.Components;
using Content.Shared.Vehicle;
using Content.Shared.Buckle.Components;
@@ -5,44 +6,64 @@ using Content.Shared.Movement.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Actions;
using Content.Shared.Audio;
using Content.Shared.Pulling.Components;
using Content.Server.Light.Components;
using Content.Server.Buckle.Components;
using Content.Server.Hands.Systems;
using Content.Shared.Tag;
using Content.Server.Mind.Components;
using Robust.Shared.Random;
using Content.Shared.Movement.Systems;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
namespace Content.Server.Vehicle
{
public sealed class VehicleSystem : EntitySystem
public sealed partial class VehicleSystem : SharedVehicleSystem
{
[Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly MovementSpeedModifierSystem _modifier = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!;
[Dependency] private readonly SharedMoverController _mover = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly RiderSystem _riderSystem = default!;
private const string KeySlot = "key_slot";
public override void Initialize()
{
base.Initialize();
InitializeRider();
SubscribeLocalEvent<VehicleComponent, HonkActionEvent>(OnHonk);
SubscribeLocalEvent<VehicleComponent, BuckleChangeEvent>(OnBuckleChange);
SubscribeLocalEvent<VehicleComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<VehicleComponent, MoveEvent>(OnMove);
SubscribeLocalEvent<VehicleComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
SubscribeLocalEvent<VehicleComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
}
/// <summary>
/// This fires when the rider presses the honk action
/// </summary>
private void OnHonk(EntityUid uid, VehicleComponent vehicle, HonkActionEvent args)
{
if (args.Handled || vehicle.HornSound == null)
return;
// TODO: Need audio refactor maybe, just some way to null it when the stream is over.
// For now better to just not loop to keep the code much cleaner.
vehicle.HonkPlayingStream?.Stop();
vehicle.HonkPlayingStream = SoundSystem.Play(vehicle.HornSound.GetSound(), Filter.Pvs(uid), uid, vehicle.HornSound.Params);
args.Handled = true;
}
/// <summary>
/// This just controls whether the wheels are turning.
/// </summary>
public override void Update(float frameTime)
{
foreach (var (vehicle, mover) in EntityQuery<VehicleComponent, SharedPlayerInputMoverComponent>())
foreach (var (vehicle, mover) in EntityQuery<VehicleComponent, InputMoverComponent>())
{
if (mover.VelocityDir.sprinting == Vector2.Zero)
if (_mover.GetVelocityInput(mover).Sprinting == Vector2.Zero)
{
UpdateAutoAnimate(vehicle.Owner, false);
continue;
@@ -50,103 +71,63 @@ namespace Content.Server.Vehicle
UpdateAutoAnimate(vehicle.Owner, true);
}
}
/// <summary>
/// Sets the initial appearance / sound, then stores the initial buckle offset and resets it.
/// </summary>
private void OnComponentInit(EntityUid uid, VehicleComponent component, ComponentInit args)
{
UpdateDrawDepth(uid, 2);
_ambientSound.SetAmbience(uid, false);
if (!TryComp<StrapComponent>(uid, out var strap))
return;
component.BaseBuckleOffset = strap.BuckleOffset;
strap.BuckleOffsetUnclamped = Vector2.Zero; //You're going to align these facing east, so...
}
/// <summary>
/// Give the user the rider component if they're buckling to the vehicle,
/// otherwise remove it.
/// </summary>
private void OnBuckleChange(EntityUid uid, VehicleComponent component, BuckleChangeEvent args)
{
// Send an event that our vehicle buckle changed
if (TryComp<MindComponent>(args.BuckledEntity, out var mind) && mind.Mind != null && mind.Mind.TryGetSession(out var session))
RaiseNetworkEvent(new BuckledToVehicleEvent(uid, args.BuckledEntity, args.Buckling), Filter.SinglePlayer(session));
// Add Rider
if (args.Buckling)
{
// Add a virtual item to rider's hand, unbuckle if we can't.
if (!_virtualItemSystem.TrySpawnVirtualItemInHand(uid, args.BuckledEntity))
{
_riderSystem.UnbuckleFromVehicle(args.BuckledEntity);
UnbuckleFromVehicle(args.BuckledEntity);
return;
}
// Set up the rider and vehicle with each other
EnsureComp<SharedPlayerInputMoverComponent>(uid);
EnsureComp<InputMoverComponent>(uid);
var rider = EnsureComp<RiderComponent>(args.BuckledEntity);
component.Rider = args.BuckledEntity;
rider.Vehicle = component;
var relay = EnsureComp<RelayInputMoverComponent>(args.BuckledEntity);
relay.RelayEntity = uid;
rider.Vehicle = uid;
component.HasRider = true;
// Handle pulling
RemComp<SharedPullableComponent>(args.BuckledEntity);
RemComp<SharedPullableComponent>(uid);
// Let this open doors if it has the key in it
if (component.HasKey)
{
_tagSystem.AddTag(uid, "DoorBumpOpener");
}
// Update appearance stuff, add actions
UpdateBuckleOffset(Transform(uid), component);
UpdateDrawDepth(uid, GetDrawDepth(Transform(uid), component.NorthOnly));
if (TryComp<ActionsComponent>(args.BuckledEntity, out var actions) && TryComp<UnpoweredFlashlightComponent>(uid, out var flashlight))
{
_actionsSystem.AddAction(args.BuckledEntity, flashlight.ToggleAction, uid, actions);
}
if (component.HornSound != null)
{
_actionsSystem.AddAction(args.BuckledEntity, component.HornAction, uid, actions);
}
_itemSlotsSystem.SetLock(uid, component.Name, true);
return;
}
// Remove rider
// Clean up actions and virtual items
_actionsSystem.RemoveProvidedActions(args.BuckledEntity, uid);
_virtualItemSystem.DeleteInHandsMatching(args.BuckledEntity, uid);
// Go back to old pullable behavior
_tagSystem.RemoveTag(uid, "DoorBumpOpener");
EnsureComp<SharedPullableComponent>(args.BuckledEntity);
EnsureComp<SharedPullableComponent>(uid);
// Entity is no longer riding
RemComp<RiderComponent>(args.BuckledEntity);
RemComp<RelayInputMoverComponent>(args.BuckledEntity);
// Reset component
component.HasRider = false;
component.Rider = null;
_itemSlotsSystem.SetLock(uid, component.Name, false);
}
/// <summary>
/// Every time the vehicle moves we update its visual and buckle positions.
/// Not the most beautiful thing but it works.
/// </summary>
private void OnMove(EntityUid uid, VehicleComponent component, ref MoveEvent args)
{
// This first check is just for safety
if (!HasComp<SharedPlayerInputMoverComponent>(uid))
{
UpdateAutoAnimate(uid, false);
return;
}
// The random check means the vehicle will stop after a few tiles without a key or without a rider
if ((!component.HasRider || !component.HasKey) && _random.Prob(0.015f))
{
RemComp<SharedPlayerInputMoverComponent>(uid);
UpdateAutoAnimate(uid, false);
}
UpdateBuckleOffset(args.Component, component);
UpdateDrawDepth(uid, GetDrawDepth(args.Component, component.NorthOnly));
}
/// <summary>
@@ -155,29 +136,19 @@ namespace Content.Server.Vehicle
/// </summary>
private void OnEntInserted(EntityUid uid, VehicleComponent component, EntInsertedIntoContainerMessage args)
{
if (args.Container.ID != KeySlot ||
!_tagSystem.HasTag(args.Entity, "VehicleKey")) return;
// Enable vehicle
var inVehicle = AddComp<InVehicleComponent>(args.Entity);
inVehicle.Vehicle = component;
if (_tagSystem.HasTag(args.Entity, "VehicleKey"))
{
// Return if the slot is not the key slot
// That slot ID should be inherited from basevehicle in the .yml
if (args.Container.ID != "key_slot")
{
return;
}
component.HasKey = true;
// This lets the vehicle move
EnsureComp<SharedPlayerInputMoverComponent>(uid);
// This lets the vehicle open doors
if (component.HasRider)
_tagSystem.AddTag(uid, "DoorBumpOpener");
component.HasKey = true;
// Audiovisual feedback
_ambientSound.SetAmbience(uid, true);
}
// Audiovisual feedback
_ambientSound.SetAmbience(uid, true);
_tagSystem.AddTag(uid, "DoorBumpOpener");
_modifier.RefreshMovementSpeedModifiers(uid);
}
/// <summary>
@@ -185,84 +156,13 @@ namespace Content.Server.Vehicle
/// </summary>
private void OnEntRemoved(EntityUid uid, VehicleComponent component, EntRemovedFromContainerMessage args)
{
RemComp<InVehicleComponent>(args.Entity);
if (args.Container.ID != KeySlot || !RemComp<InVehicleComponent>(args.Entity)) return;
if (_tagSystem.HasTag(args.Entity, "VehicleKey"))
{
component.HasKey = false;
_ambientSound.SetAmbience(uid, false);
}
}
/// <summary>
/// Depending on which direction the vehicle is facing,
/// change its draw depth. Vehicles can choose between special drawdetph
/// when facing north or south. East and west are easy.
/// </summary>
private int GetDrawDepth(TransformComponent xform, bool northOnly)
{
if (northOnly)
{
return xform.LocalRotation.Degrees switch
{
< 135f => 5,
<= 225f => 2,
_ => 5
};
}
return xform.LocalRotation.Degrees switch
{
< 45f => 5,
<= 315f => 2,
_ => 5
};
}
/// <summary>
/// Change the buckle offset based on what direction the vehicle is facing and
/// teleport any buckled entities to it. This is the most crucial part of making
/// buckled vehicles work.
/// </summary>
private void UpdateBuckleOffset(TransformComponent xform, VehicleComponent component)
{
if (!TryComp<StrapComponent>(component.Owner, out var strap))
return;
strap.BuckleOffsetUnclamped = xform.LocalRotation.Degrees switch
{
< 45f => (0, component.SouthOverride),
<= 135f => component.BaseBuckleOffset,
< 225f => (0, component.NorthOverride),
<= 315f => (component.BaseBuckleOffset.X * -1, component.BaseBuckleOffset.Y),
_ => (0, component.SouthOverride)
};
foreach (var buckledEntity in strap.BuckledEntities)
{
var buckleXform = Transform(buckledEntity);
buckleXform.LocalPosition = strap.BuckleOffset;
}
}
/// <summary>
/// Set the draw depth for the sprite.
/// </summary>
private void UpdateDrawDepth(EntityUid uid, int drawDepth)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance))
return;
appearance.SetData(VehicleVisuals.DrawDepth, drawDepth);
}
/// <summary>
/// Set whether the vehicle's base layer is animating or not.
/// </summary>
private void UpdateAutoAnimate(EntityUid uid, bool autoAnimate)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance))
return;
appearance.SetData(VehicleVisuals.AutoAnimate, autoAnimate);
// Disable vehicle
component.HasKey = false;
_ambientSound.SetAmbience(uid, false);
_tagSystem.RemoveTag(uid, "DoorBumpOpener");
_modifier.RefreshMovementSpeedModifiers(uid);
}
}
}

View File

@@ -145,7 +145,7 @@ namespace Content.Server.Zombies
_popupSystem.PopupEntity(Loc.GetString("zombie-transform", ("target", target)), target, Filter.Pvs(target));
//Make it sentient if it's an animal or something
if (!HasComp<SharedDummyInputMoverComponent>(target)) //this component is cursed and fucks shit up
if (!HasComp<InputMoverComponent>(target)) //this component is cursed and fucks shit up
MakeSentientCommand.MakeSentient(target, EntityManager);
//Make the zombie not die in the cold. Good for space zombies