Add pulling (#1409)
* Initial framework for pulling. * Make it possible to pull items via (temporary) keybind Ctrl+Click, make items follow the player correctly. * Make other objects pullable, implement functionality for moving an object being pulled, make only one object able to be pulled at a time. * Make sure that MoveTo won't allow collisions with the player * Update everything to work with the new physics engine * Update Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs Co-authored-by: ComicIronic <comicironic@gmail.com> * Physics update and convert to direct type casts * Add notnull checks * Add pull keybinds to the tutorial window * Move PullController to shared * Fix pulled items getting left behind * Fix moving pulled objects into walls * Remove flooring of coordinates when moving pulled objects * Add missing null check in PutInHand * Change pulling keybind to control and throwing to alt * Change PhysicsComponent references to IPhysicsComponent * Add trying to pull a pulled entity disabling the pull * Add pulled status effect * Fix merge conflicts * Merge fixes * Make players pullable * Fix being able to pull yourself * Change pull moving to use a velocity * Update pulled and pulling icons to not be buckle A tragedy * Make pulled and pulling icons more consistent * Remove empty not pulled and not pulling images * Pulled icon update * Pulled icon update * Add clicking pulling status effect to stop the pull * Fix spacewalking when pulling * Merge conflict fixes * Add a pull verb * Fix nullable error * Add pulling through the entity drop down menu Co-authored-by: Jackson Lewis <inquisitivepenguin@protonmail.com> Co-authored-by: ComicIronic <comicironic@gmail.com>
This commit is contained in:
@@ -14,6 +14,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Content.Client.GameObjects.Components.Items
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent
|
||||
{
|
||||
private HandsGui? _gui;
|
||||
|
||||
@@ -447,7 +447,9 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
}
|
||||
|
||||
if (args.Function == EngineKeyFunctions.Use ||
|
||||
args.Function == ContentKeyFunctions.Point)
|
||||
args.Function == ContentKeyFunctions.Point ||
|
||||
args.Function == ContentKeyFunctions.TryPullObject ||
|
||||
args.Function == ContentKeyFunctions.MovePulledObject)
|
||||
{
|
||||
// TODO: Remove an entity from the menu when it is deleted
|
||||
if (_entity.Deleted)
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
"TrashSpawner",
|
||||
"Pill",
|
||||
"RCD",
|
||||
"Pullable",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
|
||||
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
||||
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.TryPullObject);
|
||||
human.AddFunction(ContentKeyFunctions.MovePulledObject);
|
||||
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
||||
@@ -36,6 +38,8 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
||||
human.AddFunction(ContentKeyFunctions.WideAttack);
|
||||
human.AddFunction(ContentKeyFunctions.Point);
|
||||
human.AddFunction(ContentKeyFunctions.TryPullObject);
|
||||
human.AddFunction(ContentKeyFunctions.MovePulledObject);
|
||||
|
||||
var ghost = contexts.New("ghost", "common");
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||
|
||||
@@ -81,6 +81,8 @@ Use hand/object in hand: [color=#a4885c]{22}[/color]
|
||||
Do wide attack: [color=#a4885c]{23}[/color]
|
||||
Use targeted entity: [color=#a4885c]{11}[/color]
|
||||
Throw held item: [color=#a4885c]{12}[/color]
|
||||
Pull entity: [color=#a4885c]{30}[/color]
|
||||
Move pulled entity: [color=#a4885c]{29}[/color]
|
||||
Examine entity: [color=#a4885c]{13}[/color]
|
||||
Point somewhere: [color=#a4885c]{28}[/color]
|
||||
Open entity context menu: [color=#a4885c]{14}[/color]
|
||||
@@ -116,7 +118,9 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]",
|
||||
Key(SmartEquipBelt),
|
||||
Key(FocusOOC),
|
||||
Key(FocusAdminChat),
|
||||
Key(Point)));
|
||||
Key(Point),
|
||||
Key(TryPullObject),
|
||||
Key(MovePulledObject)));
|
||||
|
||||
//Gameplay
|
||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
|
||||
|
||||
@@ -3,11 +3,15 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Server.GameObjects.EntitySystems.Click;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Shared.BodySystem;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||
@@ -27,6 +31,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IHandsComponent))]
|
||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
||||
{
|
||||
#pragma warning disable 649
|
||||
@@ -490,6 +495,34 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
return false;
|
||||
}
|
||||
|
||||
public void StartPull(PullableComponent pullable)
|
||||
{
|
||||
if (Owner == pullable.Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPulling)
|
||||
{
|
||||
StopPull();
|
||||
}
|
||||
|
||||
PulledObject = pullable.Owner.GetComponent<ICollidableComponent>();
|
||||
var controller = PulledObject!.EnsureController<PullController>();
|
||||
controller!.StartPull(Owner.GetComponent<ICollidableComponent>());
|
||||
|
||||
AddPullingStatuses();
|
||||
}
|
||||
|
||||
public void MovePulledObject(GridCoordinates puller, GridCoordinates to)
|
||||
{
|
||||
if (PulledObject != null &&
|
||||
PulledObject.TryGetController(out PullController controller))
|
||||
{
|
||||
controller.TryMoveTo(puller, to);
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
@@ -600,6 +633,42 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPullingStatuses()
|
||||
{
|
||||
if (PulledObject?.Owner != null &&
|
||||
PulledObject.Owner.TryGetComponent(out ServerStatusEffectsComponent pulledStatus))
|
||||
{
|
||||
pulledStatus.ChangeStatusEffectIcon(StatusEffect.Pulled,
|
||||
"/Textures/Interface/StatusEffects/Pull/pulled.png");
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent ownerStatus))
|
||||
{
|
||||
ownerStatus.ChangeStatusEffectIcon(StatusEffect.Pulling,
|
||||
"/Textures/Interface/StatusEffects/Pull/pulling.png");
|
||||
}
|
||||
}
|
||||
|
||||
private void RemovePullingStatuses()
|
||||
{
|
||||
if (PulledObject?.Owner != null &&
|
||||
PulledObject.Owner.TryGetComponent(out ServerStatusEffectsComponent pulledStatus))
|
||||
{
|
||||
pulledStatus.RemoveStatusEffect(StatusEffect.Pulled);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent ownerStatus))
|
||||
{
|
||||
ownerStatus.RemoveStatusEffect(StatusEffect.Pulling);
|
||||
}
|
||||
}
|
||||
|
||||
public override void StopPull()
|
||||
{
|
||||
RemovePullingStatuses();
|
||||
base.StopPull();
|
||||
}
|
||||
|
||||
void IBodyPartAdded.BodyPartAdded(BodyPartAddedEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Part.PartType != BodyPartType.Hand)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Buckle;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -104,6 +105,14 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
|
||||
controller.RemoveController();
|
||||
break;
|
||||
case StatusEffect.Pulling:
|
||||
if (!player.TryGetComponent(out HandsComponent hands))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hands.StopPull();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class PullableComponent: Component
|
||||
{
|
||||
public override string Name => "Pullable";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Timing;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
@@ -9,11 +11,13 @@ using Content.Shared.GameObjects.EntitySystemMessages;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Physics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -48,6 +52,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
new PointerInputCmdHandler(HandleWideAttack))
|
||||
.Bind(ContentKeyFunctions.ActivateItemInWorld,
|
||||
new PointerInputCmdHandler(HandleActivateItemInWorld))
|
||||
.Bind(ContentKeyFunctions.TryPullObject, new PointerInputCmdHandler(HandleTryPullObject))
|
||||
.Register<InteractionSystem>();
|
||||
}
|
||||
|
||||
@@ -221,6 +226,72 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HandleTryPullObject(ICommonSession session, GridCoordinates coords, EntityUid uid)
|
||||
{
|
||||
// client sanitization
|
||||
if (!_mapManager.GridExists(coords.GridID))
|
||||
{
|
||||
Logger.InfoS("system.interaction", $"Invalid Coordinates for pulling: client={session}, coords={coords}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uid.IsClientSide())
|
||||
{
|
||||
Logger.WarningS("system.interaction",
|
||||
$"Client sent pull interaction with client-side entity. Session={session}, Uid={uid}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
{
|
||||
Logger.WarningS("system.interaction",
|
||||
$"Client sent pulling interaction with no attached entity. Session={session}, Uid={uid}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetEntity(uid, out var pulledObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player == pulledObject)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pulledObject.TryGetComponent<PullableComponent>(out var pull))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dist = player.Transform.GridPosition.Position - pulledObject.Transform.GridPosition.Position;
|
||||
if (dist.LengthSquared > InteractionRangeSquared)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var physics = pull.Owner.GetComponent<IPhysicsComponent>();
|
||||
var controller = physics.EnsureController<PullController>();
|
||||
|
||||
if (controller.GettingPulled)
|
||||
{
|
||||
hands.StopPull();
|
||||
}
|
||||
else
|
||||
{
|
||||
hands.StartPull(pull);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid)
|
||||
{
|
||||
// Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
|
||||
|
||||
@@ -52,7 +52,9 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
||||
.Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
|
||||
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
||||
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt)).Register<HandsSystem>();
|
||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
|
||||
.Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject))
|
||||
.Register<HandsSystem>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -199,13 +201,16 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
||||
if (plyEnt == null || !plyEnt.IsValid())
|
||||
return;
|
||||
|
||||
if (!plyEnt.TryGetComponent(out HandsComponent handsComp) || !plyEnt.TryGetComponent(out InventoryComponent inventoryComp))
|
||||
if (!plyEnt.TryGetComponent(out HandsComponent handsComp) ||
|
||||
!plyEnt.TryGetComponent(out InventoryComponent inventoryComp))
|
||||
return;
|
||||
|
||||
if (!inventoryComp.TryGetSlotItem(equipementSlot, out ItemComponent equipmentItem)
|
||||
|| !equipmentItem.Owner.TryGetComponent<ServerStorageComponent>(out var storageComponent))
|
||||
{
|
||||
_notifyManager.PopupMessage(plyEnt, plyEnt, Loc.GetString("You have no {0} to take something out of!", EquipmentSlotDefines.SlotNames[equipementSlot].ToLower()));
|
||||
_notifyManager.PopupMessage(plyEnt, plyEnt,
|
||||
Loc.GetString("You have no {0} to take something out of!",
|
||||
EquipmentSlotDefines.SlotNames[equipementSlot].ToLower()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -219,7 +224,9 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
||||
{
|
||||
if (storageComponent.StoredEntities.Count == 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(plyEnt, plyEnt, Loc.GetString("There's nothing in your {0} to take out!", EquipmentSlotDefines.SlotNames[equipementSlot].ToLower()));
|
||||
_notifyManager.PopupMessage(plyEnt, plyEnt,
|
||||
Loc.GetString("There's nothing in your {0} to take out!",
|
||||
EquipmentSlotDefines.SlotNames[equipementSlot].ToLower()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -229,5 +236,20 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleMovePulledObject(ICommonSession session, GridCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var playerEntity = session.AttachedEntity;
|
||||
|
||||
if (playerEntity == null ||
|
||||
!playerEntity.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hands.MovePulledObject(playerEntity.Transform.GridPosition, coords);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
76
Content.Server/GlobalVerbs/PullingVerb.cs
Normal file
76
Content.Server/GlobalVerbs/PullingVerb.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.GlobalVerbs
|
||||
{
|
||||
/// <summary>
|
||||
/// Global verb that pulls an entity.
|
||||
/// </summary>
|
||||
[GlobalVerb]
|
||||
public class PullingVerb : GlobalVerb
|
||||
{
|
||||
public override bool RequireInteractionRange => false;
|
||||
|
||||
public override void GetData(IEntity user, IEntity target, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (user == target ||
|
||||
!user.HasComponent<IActorComponent>() ||
|
||||
!target.HasComponent<PullableComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dist = user.Transform.GridPosition.Position - target.Transform.GridPosition.Position;
|
||||
if (dist.LengthSquared > SharedInteractionSystem.InteractionRangeSquared)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.HasComponent<ISharedHandsComponent>() ||
|
||||
!user.TryGetComponent(out ICollidableComponent userCollidable) ||
|
||||
!target.TryGetComponent(out ICollidableComponent targetCollidable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var controller = targetCollidable.EnsureController<PullController>();
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = controller.Puller == userCollidable
|
||||
? Loc.GetString("Stop pulling")
|
||||
: Loc.GetString("Pull");
|
||||
}
|
||||
|
||||
public override void Activate(IEntity user, IEntity target)
|
||||
{
|
||||
if (!user.TryGetComponent(out ICollidableComponent userCollidable) ||
|
||||
!target.TryGetComponent(out ICollidableComponent targetCollidable) ||
|
||||
!target.TryGetComponent(out PullableComponent pullable) ||
|
||||
!user.TryGetComponent(out HandsComponent hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var controller = targetCollidable.EnsureController<PullController>();
|
||||
|
||||
if (controller.Puller == userCollidable)
|
||||
{
|
||||
hands.StopPull();
|
||||
}
|
||||
else
|
||||
{
|
||||
hands.StartPull(pullable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||
@@ -9,7 +10,7 @@ using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects.Components.Items
|
||||
{
|
||||
public interface IHandsComponent : IComponent
|
||||
public interface IHandsComponent : ISharedHandsComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The hand name of the currently active hand.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Items
|
||||
{
|
||||
public interface ISharedHandsComponent : IComponent
|
||||
{
|
||||
void StopPull();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Items
|
||||
{
|
||||
public abstract class SharedHandsComponent : Component
|
||||
public abstract class SharedHandsComponent : Component, ISharedHandsComponent
|
||||
{
|
||||
public sealed override string Name => "Hands";
|
||||
public sealed override uint? NetID => ContentNetIDs.HANDS;
|
||||
|
||||
[ViewVariables]
|
||||
protected ICollidableComponent? PulledObject;
|
||||
|
||||
[ViewVariables]
|
||||
protected bool IsPulling => PulledObject != null;
|
||||
|
||||
public virtual void StopPull()
|
||||
{
|
||||
if (PulledObject != null &&
|
||||
PulledObject.TryGetController(out PullController controller))
|
||||
{
|
||||
controller.StopPull();
|
||||
}
|
||||
|
||||
PulledObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
@@ -35,9 +53,9 @@ namespace Content.Shared.GameObjects.Components.Items
|
||||
public class HandsComponentState : ComponentState
|
||||
{
|
||||
public readonly SharedHand[] Hands;
|
||||
public readonly string ActiveIndex;
|
||||
public readonly string? ActiveIndex;
|
||||
|
||||
public HandsComponentState(SharedHand[] hands, string activeIndex) : base(ContentNetIDs.HANDS)
|
||||
public HandsComponentState(SharedHand[] hands, string? activeIndex) : base(ContentNetIDs.HANDS)
|
||||
{
|
||||
Hands = hands;
|
||||
ActiveIndex = activeIndex;
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
Thirst,
|
||||
Stun,
|
||||
Buckled,
|
||||
Piloting
|
||||
Piloting,
|
||||
Pulling,
|
||||
Pulled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,12 @@ namespace Content.Shared.GameObjects.EntitySystems
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't count pulled entities
|
||||
if (otherCollider.HasController<PullController>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Item check.
|
||||
var touching = ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0
|
||||
|| (otherCollider.CollisionMask & collider.CollisionLayer) != 0x0) // Ensure collision
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction OpenTutorial = "OpenTutorial";
|
||||
public static readonly BoundKeyFunction SwapHands = "SwapHands";
|
||||
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
||||
public static readonly BoundKeyFunction TryPullObject = "TryPullObject";
|
||||
public static readonly BoundKeyFunction MovePulledObject = "MovePulledObject";
|
||||
public static readonly BoundKeyFunction ToggleCombatMode = "ToggleCombatMode";
|
||||
public static readonly BoundKeyFunction MouseMiddle = "MouseMiddle";
|
||||
public static readonly BoundKeyFunction OpenEntitySpawnWindow = "OpenEntitySpawnWindow";
|
||||
|
||||
114
Content.Shared/Physics/PullController.cs
Normal file
114
Content.Shared/Physics/PullController.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Shared.Physics
|
||||
{
|
||||
public class PullController : VirtualController
|
||||
{
|
||||
private const float DistBeforePull = 1.0f;
|
||||
|
||||
private const float DistBeforeStopPull = SharedInteractionSystem.InteractionRange;
|
||||
|
||||
private ICollidableComponent? _puller;
|
||||
|
||||
public bool GettingPulled => _puller != null;
|
||||
|
||||
private GridCoordinates? _movingTo;
|
||||
|
||||
public ICollidableComponent? Puller => _puller;
|
||||
|
||||
public void StartPull(ICollidableComponent? pull)
|
||||
{
|
||||
_puller = pull;
|
||||
}
|
||||
|
||||
public void StopPull()
|
||||
{
|
||||
_puller = null;
|
||||
ControlledComponent?.TryRemoveController<PullController>();
|
||||
}
|
||||
|
||||
public void TryMoveTo(GridCoordinates from, GridCoordinates to)
|
||||
{
|
||||
if (_puller == null || ControlledComponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
if (!from.InRange(mapManager, to, SharedInteractionSystem.InteractionRange))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dist = _puller.Owner.Transform.GridPosition.Position - to.Position;
|
||||
|
||||
if (Math.Sqrt(dist.LengthSquared) > DistBeforeStopPull ||
|
||||
Math.Sqrt(dist.LengthSquared) < 0.25f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_movingTo = to;
|
||||
}
|
||||
|
||||
public override void UpdateBeforeProcessing()
|
||||
{
|
||||
if (_puller == null || ControlledComponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we outside of pulling range?
|
||||
var dist = _puller.Owner.Transform.WorldPosition - ControlledComponent.Owner.Transform.WorldPosition;
|
||||
|
||||
if (dist.Length > DistBeforeStopPull)
|
||||
{
|
||||
_puller.Owner.GetComponent<ISharedHandsComponent>().StopPull();
|
||||
}
|
||||
else if (_movingTo.HasValue)
|
||||
{
|
||||
var diff = _movingTo.Value.Position - ControlledComponent.Owner.Transform.GridPosition.Position;
|
||||
LinearVelocity = diff.Normalized * 5;
|
||||
}
|
||||
else if (dist.Length > DistBeforePull)
|
||||
{
|
||||
LinearVelocity = dist.Normalized * _puller.LinearVelocity.Length * 1.1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
LinearVelocity = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateAfterProcessing()
|
||||
{
|
||||
base.UpdateAfterProcessing();
|
||||
|
||||
if (ControlledComponent == null)
|
||||
{
|
||||
_movingTo = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_movingTo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ControlledComponent.Owner.Transform.GridPosition.Position.EqualsApprox(_movingTo.Value.Position, 0.01))
|
||||
{
|
||||
_movingTo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@
|
||||
state_closed: generic_door
|
||||
- type: LoopingSound
|
||||
- type: Anchorable
|
||||
- type: Pullable
|
||||
placement:
|
||||
snap:
|
||||
- Wall
|
||||
|
||||
@@ -47,3 +47,4 @@
|
||||
state_open: crate_open
|
||||
state_closed: crate_door
|
||||
- type: LoopingSound
|
||||
- type: Pullable
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
arc: fist
|
||||
- type: Grammar
|
||||
proper: true
|
||||
- type: Pullable
|
||||
|
||||
- type: entity
|
||||
save: false
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
mass: 5
|
||||
- type: Sprite
|
||||
drawdepth: Items
|
||||
- type: Pullable
|
||||
|
||||
BIN
Resources/Textures/Interface/StatusEffects/Pull/pulled.png
Normal file
BIN
Resources/Textures/Interface/StatusEffects/Pull/pulled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
Resources/Textures/Interface/StatusEffects/Pull/pulling.png
Normal file
BIN
Resources/Textures/Interface/StatusEffects/Pull/pulling.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 525 B |
@@ -102,6 +102,15 @@ binds:
|
||||
type: state
|
||||
key: MouseLeft
|
||||
canFocus: true
|
||||
mod1: Alt
|
||||
- function: TryPullObject
|
||||
type: state
|
||||
canFocus: true
|
||||
key: MouseLeft
|
||||
mod1: Control
|
||||
- function: MovePulledObject
|
||||
type: state
|
||||
key: MouseRight
|
||||
mod1: Control
|
||||
- function: OpenContextMenu
|
||||
type: state
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=placeable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=preemptively/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=prefs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pullable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Soundfont/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=soundfonts/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
Reference in New Issue
Block a user