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
|
namespace Content.Client.GameObjects.Components.Items
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||||
public class HandsComponent : SharedHandsComponent
|
public class HandsComponent : SharedHandsComponent
|
||||||
{
|
{
|
||||||
private HandsGui? _gui;
|
private HandsGui? _gui;
|
||||||
|
|||||||
@@ -447,7 +447,9 @@ namespace Content.Client.GameObjects.EntitySystems
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (args.Function == EngineKeyFunctions.Use ||
|
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
|
// TODO: Remove an entity from the menu when it is deleted
|
||||||
if (_entity.Deleted)
|
if (_entity.Deleted)
|
||||||
|
|||||||
@@ -136,6 +136,7 @@
|
|||||||
"TrashSpawner",
|
"TrashSpawner",
|
||||||
"Pill",
|
"Pill",
|
||||||
"RCD",
|
"RCD",
|
||||||
|
"Pullable",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ namespace Content.Client.Input
|
|||||||
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
|
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
|
||||||
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
||||||
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
||||||
|
human.AddFunction(ContentKeyFunctions.TryPullObject);
|
||||||
|
human.AddFunction(ContentKeyFunctions.MovePulledObject);
|
||||||
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||||
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
|
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
|
||||||
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
||||||
@@ -36,6 +38,8 @@ namespace Content.Client.Input
|
|||||||
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
||||||
human.AddFunction(ContentKeyFunctions.WideAttack);
|
human.AddFunction(ContentKeyFunctions.WideAttack);
|
||||||
human.AddFunction(ContentKeyFunctions.Point);
|
human.AddFunction(ContentKeyFunctions.Point);
|
||||||
|
human.AddFunction(ContentKeyFunctions.TryPullObject);
|
||||||
|
human.AddFunction(ContentKeyFunctions.MovePulledObject);
|
||||||
|
|
||||||
var ghost = contexts.New("ghost", "common");
|
var ghost = contexts.New("ghost", "common");
|
||||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ Use hand/object in hand: [color=#a4885c]{22}[/color]
|
|||||||
Do wide attack: [color=#a4885c]{23}[/color]
|
Do wide attack: [color=#a4885c]{23}[/color]
|
||||||
Use targeted entity: [color=#a4885c]{11}[/color]
|
Use targeted entity: [color=#a4885c]{11}[/color]
|
||||||
Throw held item: [color=#a4885c]{12}[/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]
|
Examine entity: [color=#a4885c]{13}[/color]
|
||||||
Point somewhere: [color=#a4885c]{28}[/color]
|
Point somewhere: [color=#a4885c]{28}[/color]
|
||||||
Open entity context menu: [color=#a4885c]{14}[/color]
|
Open entity context menu: [color=#a4885c]{14}[/color]
|
||||||
@@ -116,7 +118,9 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]",
|
|||||||
Key(SmartEquipBelt),
|
Key(SmartEquipBelt),
|
||||||
Key(FocusOOC),
|
Key(FocusOOC),
|
||||||
Key(FocusAdminChat),
|
Key(FocusAdminChat),
|
||||||
Key(Point)));
|
Key(Point),
|
||||||
|
Key(TryPullObject),
|
||||||
|
Key(MovePulledObject)));
|
||||||
|
|
||||||
//Gameplay
|
//Gameplay
|
||||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
|
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
|
using Content.Server.GameObjects.Components.Movement;
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||||
using Content.Shared.GameObjects.Components.Items;
|
using Content.Shared.GameObjects.Components.Items;
|
||||||
using Content.Server.GameObjects.EntitySystems.Click;
|
using Content.Server.GameObjects.EntitySystems.Click;
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
||||||
using Content.Shared.BodySystem;
|
using Content.Shared.BodySystem;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Content.Shared.Physics;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.GameObjects.Components.Container;
|
using Robust.Server.GameObjects.Components.Container;
|
||||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||||
@@ -27,6 +31,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
|||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IHandsComponent))]
|
[ComponentReference(typeof(IHandsComponent))]
|
||||||
|
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
||||||
{
|
{
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
@@ -490,6 +495,34 @@ namespace Content.Server.GameObjects.Components.GUI
|
|||||||
return false;
|
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)
|
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||||
{
|
{
|
||||||
base.HandleNetworkMessage(message, channel, session);
|
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)
|
void IBodyPartAdded.BodyPartAdded(BodyPartAddedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Part.PartType != BodyPartType.Hand)
|
if (eventArgs.Part.PartType != BodyPartType.Hand)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.GameObjects.Components.Buckle;
|
using Content.Server.GameObjects.Components.Buckle;
|
||||||
|
using Content.Server.GameObjects.Components.GUI;
|
||||||
using Content.Server.GameObjects.Components.Movement;
|
using Content.Server.GameObjects.Components.Movement;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -104,6 +105,14 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
|
|
||||||
controller.RemoveController();
|
controller.RemoveController();
|
||||||
break;
|
break;
|
||||||
|
case StatusEffect.Pulling:
|
||||||
|
if (!player.TryGetComponent(out HandsComponent hands))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hands.StopPull();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.Components.GUI;
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
|
using Content.Server.GameObjects.Components.Movement;
|
||||||
using Content.Server.GameObjects.Components.Timing;
|
using Content.Server.GameObjects.Components.Timing;
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||||
using Content.Server.Utility;
|
using Content.Server.Utility;
|
||||||
@@ -9,11 +11,13 @@ using Content.Shared.GameObjects.EntitySystemMessages;
|
|||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
|
using Content.Shared.Physics;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.Interfaces.Player;
|
using Robust.Server.Interfaces.Player;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Components;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
@@ -48,6 +52,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
|||||||
new PointerInputCmdHandler(HandleWideAttack))
|
new PointerInputCmdHandler(HandleWideAttack))
|
||||||
.Bind(ContentKeyFunctions.ActivateItemInWorld,
|
.Bind(ContentKeyFunctions.ActivateItemInWorld,
|
||||||
new PointerInputCmdHandler(HandleActivateItemInWorld))
|
new PointerInputCmdHandler(HandleActivateItemInWorld))
|
||||||
|
.Bind(ContentKeyFunctions.TryPullObject, new PointerInputCmdHandler(HandleTryPullObject))
|
||||||
.Register<InteractionSystem>();
|
.Register<InteractionSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +226,72 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
|||||||
return true;
|
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)
|
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
|
// 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.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
|
||||||
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
.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 />
|
/// <inheritdoc />
|
||||||
@@ -199,13 +201,16 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
|||||||
if (plyEnt == null || !plyEnt.IsValid())
|
if (plyEnt == null || !plyEnt.IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!plyEnt.TryGetComponent(out HandsComponent handsComp) || !plyEnt.TryGetComponent(out InventoryComponent inventoryComp))
|
if (!plyEnt.TryGetComponent(out HandsComponent handsComp) ||
|
||||||
|
!plyEnt.TryGetComponent(out InventoryComponent inventoryComp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!inventoryComp.TryGetSlotItem(equipementSlot, out ItemComponent equipmentItem)
|
if (!inventoryComp.TryGetSlotItem(equipementSlot, out ItemComponent equipmentItem)
|
||||||
|| !equipmentItem.Owner.TryGetComponent<ServerStorageComponent>(out var storageComponent))
|
|| !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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +224,9 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
|||||||
{
|
{
|
||||||
if (storageComponent.StoredEntities.Count == 0)
|
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
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.GameObjects.Components;
|
using Content.Server.GameObjects.Components;
|
||||||
|
using Content.Shared.GameObjects.Components.Items;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Robust.Server.GameObjects.Components.Container;
|
using Robust.Server.GameObjects.Components.Container;
|
||||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||||
@@ -9,7 +10,7 @@ using Robust.Shared.Map;
|
|||||||
|
|
||||||
namespace Content.Server.Interfaces.GameObjects.Components.Items
|
namespace Content.Server.Interfaces.GameObjects.Components.Items
|
||||||
{
|
{
|
||||||
public interface IHandsComponent : IComponent
|
public interface IHandsComponent : ISharedHandsComponent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hand name of the currently active hand.
|
/// 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;
|
#nullable enable
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using JetBrains.Annotations;
|
using Content.Shared.Physics;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.GameObjects.Components;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components.Items
|
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 string Name => "Hands";
|
||||||
public sealed override uint? NetID => ContentNetIDs.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]
|
[Serializable, NetSerializable]
|
||||||
@@ -35,9 +53,9 @@ namespace Content.Shared.GameObjects.Components.Items
|
|||||||
public class HandsComponentState : ComponentState
|
public class HandsComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public readonly SharedHand[] Hands;
|
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;
|
Hands = hands;
|
||||||
ActiveIndex = activeIndex;
|
ActiveIndex = activeIndex;
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
|||||||
Thirst,
|
Thirst,
|
||||||
Stun,
|
Stun,
|
||||||
Buckled,
|
Buckled,
|
||||||
Piloting
|
Piloting,
|
||||||
|
Pulling,
|
||||||
|
Pulled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,12 @@ namespace Content.Shared.GameObjects.EntitySystems
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't count pulled entities
|
||||||
|
if (otherCollider.HasController<PullController>())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Item check.
|
// TODO: Item check.
|
||||||
var touching = ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0
|
var touching = ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0
|
||||||
|| (otherCollider.CollisionMask & collider.CollisionLayer) != 0x0) // Ensure collision
|
|| (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 OpenTutorial = "OpenTutorial";
|
||||||
public static readonly BoundKeyFunction SwapHands = "SwapHands";
|
public static readonly BoundKeyFunction SwapHands = "SwapHands";
|
||||||
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
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 ToggleCombatMode = "ToggleCombatMode";
|
||||||
public static readonly BoundKeyFunction MouseMiddle = "MouseMiddle";
|
public static readonly BoundKeyFunction MouseMiddle = "MouseMiddle";
|
||||||
public static readonly BoundKeyFunction OpenEntitySpawnWindow = "OpenEntitySpawnWindow";
|
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
|
state_closed: generic_door
|
||||||
- type: LoopingSound
|
- type: LoopingSound
|
||||||
- type: Anchorable
|
- type: Anchorable
|
||||||
|
- type: Pullable
|
||||||
placement:
|
placement:
|
||||||
snap:
|
snap:
|
||||||
- Wall
|
- Wall
|
||||||
|
|||||||
@@ -47,3 +47,4 @@
|
|||||||
state_open: crate_open
|
state_open: crate_open
|
||||||
state_closed: crate_door
|
state_closed: crate_door
|
||||||
- type: LoopingSound
|
- type: LoopingSound
|
||||||
|
- type: Pullable
|
||||||
|
|||||||
@@ -138,6 +138,7 @@
|
|||||||
arc: fist
|
arc: fist
|
||||||
- type: Grammar
|
- type: Grammar
|
||||||
proper: true
|
proper: true
|
||||||
|
- type: Pullable
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
save: false
|
save: false
|
||||||
|
|||||||
@@ -19,3 +19,4 @@
|
|||||||
mass: 5
|
mass: 5
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: Items
|
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
|
type: state
|
||||||
key: MouseLeft
|
key: MouseLeft
|
||||||
canFocus: true
|
canFocus: true
|
||||||
|
mod1: Alt
|
||||||
|
- function: TryPullObject
|
||||||
|
type: state
|
||||||
|
canFocus: true
|
||||||
|
key: MouseLeft
|
||||||
|
mod1: Control
|
||||||
|
- function: MovePulledObject
|
||||||
|
type: state
|
||||||
|
key: MouseRight
|
||||||
mod1: Control
|
mod1: Control
|
||||||
- function: OpenContextMenu
|
- function: OpenContextMenu
|
||||||
type: state
|
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/=placeable/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=preemptively/@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/=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/=Serilog/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Soundfont/@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>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=soundfonts/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
Reference in New Issue
Block a user