committed by
GitHub
parent
8038ebe37d
commit
b0f212bad5
@@ -74,6 +74,7 @@
|
|||||||
<Compile Include="GameObjects\Components\Power\ApcBoundUserInterface.cs" />
|
<Compile Include="GameObjects\Components\Power\ApcBoundUserInterface.cs" />
|
||||||
<Compile Include="GameObjects\Components\Power\PowerCellVisualizer2D.cs" />
|
<Compile Include="GameObjects\Components\Power\PowerCellVisualizer2D.cs" />
|
||||||
<Compile Include="GameObjects\Components\Storage\ClientStorageComponent.cs" />
|
<Compile Include="GameObjects\Components\Storage\ClientStorageComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\EntitySystems\VerbSystem.cs" />
|
||||||
<Compile Include="Input\ContentContexts.cs" />
|
<Compile Include="Input\ContentContexts.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="GameObjects\Components\Items\ClientHandsComponent.cs" />
|
<Compile Include="GameObjects\Components\Items\ClientHandsComponent.cs" />
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
using SS14.Client.Interfaces.GameObjects.Components;
|
using SS14.Client.Interfaces.GameObjects.Components;
|
||||||
using SS14.Shared.GameObjects;
|
using SS14.Shared.GameObjects;
|
||||||
using SS14.Shared.GameObjects.Components.Transform;
|
using SS14.Shared.GameObjects.Components.Transform;
|
||||||
|
|||||||
@@ -174,6 +174,11 @@ namespace Content.Client.GameObjects
|
|||||||
SendNetworkMessage(new ClientChangedHandMsg(index));
|
SendNetworkMessage(new ClientChangedHandMsg(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AttackByInHand(string index)
|
||||||
|
{
|
||||||
|
SendNetworkMessage(new ClientAttackByInHandMsg(index));
|
||||||
|
}
|
||||||
|
|
||||||
public void UseActiveHand()
|
public void UseActiveHand()
|
||||||
{
|
{
|
||||||
if (GetEntity(ActiveIndex) != null)
|
if (GetEntity(ActiveIndex) != null)
|
||||||
|
|||||||
235
Content.Client/GameObjects/EntitySystems/VerbSystem.cs
Normal file
235
Content.Client/GameObjects/EntitySystems/VerbSystem.cs
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.EntitySystemMessages;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using SS14.Client.GameObjects.EntitySystems;
|
||||||
|
using SS14.Client.Interfaces.Input;
|
||||||
|
using SS14.Client.Interfaces.State;
|
||||||
|
using SS14.Client.Interfaces.UserInterface;
|
||||||
|
using SS14.Client.Player;
|
||||||
|
using SS14.Client.State.States;
|
||||||
|
using SS14.Client.UserInterface.Controls;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.GameObjects.Systems;
|
||||||
|
using SS14.Shared.Input;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
using SS14.Shared.Interfaces.Network;
|
||||||
|
using SS14.Shared.IoC;
|
||||||
|
using SS14.Shared.Log;
|
||||||
|
using SS14.Shared.Map;
|
||||||
|
using SS14.Shared.Maths;
|
||||||
|
using SS14.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
public class VerbSystem : EntitySystem
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IStateManager _stateManager;
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager;
|
||||||
|
[Dependency] private readonly IInputManager _inputManager;
|
||||||
|
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private Popup _currentPopup;
|
||||||
|
private EntityUid _currentEntity;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
var input = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||||
|
input.BindMap.BindFunction(ContentKeyFunctions.OpenContextMenu,
|
||||||
|
new PointerInputCmdHandler(OnOpenContextMenu));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RegisterMessageTypes()
|
||||||
|
{
|
||||||
|
base.RegisterMessageTypes();
|
||||||
|
|
||||||
|
RegisterMessageType<VerbSystemMessages.VerbsResponseMessage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates)
|
||||||
|
{
|
||||||
|
if (_currentPopup != null)
|
||||||
|
{
|
||||||
|
_closeContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentEntity = entity.Uid;
|
||||||
|
_currentPopup = new Popup();
|
||||||
|
_currentPopup.UserInterfaceManager.StateRoot.AddChild(_currentPopup);
|
||||||
|
_currentPopup.OnPopupHide += _closeContextMenu;
|
||||||
|
var vBox = new VBoxContainer("ButtonBox");
|
||||||
|
_currentPopup.AddChild(vBox);
|
||||||
|
|
||||||
|
vBox.AddChild(new Label {Text = "Waiting on Server..."});
|
||||||
|
RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity));
|
||||||
|
|
||||||
|
var size = vBox.CombinedMinimumSize;
|
||||||
|
var box = UIBox2.FromDimensions(screenCoordinates.AsVector, size);
|
||||||
|
_currentPopup.Open(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||||
|
{
|
||||||
|
if (_currentPopup != null)
|
||||||
|
{
|
||||||
|
_closeContextMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(_stateManager.CurrentState is GameScreen gameScreen))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entities = gameScreen.GetEntitiesUnderPosition(args.Coordinates);
|
||||||
|
|
||||||
|
_currentPopup = new Popup();
|
||||||
|
_currentPopup.OnPopupHide += _closeContextMenu;
|
||||||
|
var vBox = new VBoxContainer("ButtonBox");
|
||||||
|
_currentPopup.AddChild(vBox);
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
var button = new Button {Text = entity.Name};
|
||||||
|
vBox.AddChild(button);
|
||||||
|
button.OnPressed += _ => OnContextButtonPressed(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPopup.UserInterfaceManager.StateRoot.AddChild(_currentPopup);
|
||||||
|
|
||||||
|
var size = vBox.CombinedMinimumSize;
|
||||||
|
var box = UIBox2.FromDimensions(args.ScreenCoordinates.Position, size);
|
||||||
|
_currentPopup.Open(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnContextButtonPressed(IEntity entity)
|
||||||
|
{
|
||||||
|
OpenContextMenu(entity, new ScreenCoordinates(_inputManager.MouseScreenPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
|
||||||
|
{
|
||||||
|
base.HandleNetMessage(channel, message);
|
||||||
|
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case VerbSystemMessages.VerbsResponseMessage resp:
|
||||||
|
_fillEntityPopup(resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _fillEntityPopup(VerbSystemMessages.VerbsResponseMessage msg)
|
||||||
|
{
|
||||||
|
if (_currentEntity != msg.Entity || !_entityManager.TryGetEntity(_currentEntity, out var entity))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.AssertNotNull(_currentPopup);
|
||||||
|
|
||||||
|
var buttons = new List<Button>();
|
||||||
|
|
||||||
|
var vBox = _currentPopup.GetChild<VBoxContainer>("ButtonBox");
|
||||||
|
vBox.DisposeAllChildren();
|
||||||
|
foreach (var data in msg.Verbs)
|
||||||
|
{
|
||||||
|
var button = new Button {Text = data.Text, Disabled = !data.Available};
|
||||||
|
if (data.Available)
|
||||||
|
{
|
||||||
|
button.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(_currentEntity, data.Key));
|
||||||
|
_closeContextMenu();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.Add(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = GetUserEntity();
|
||||||
|
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
|
||||||
|
{
|
||||||
|
if (verb.RequireInteractionRange)
|
||||||
|
{
|
||||||
|
var distanceSquared = (user.Transform.WorldPosition - entity.Transform.WorldPosition)
|
||||||
|
.LengthSquared;
|
||||||
|
if (distanceSquared > Verb.InteractionRangeSquared)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var disabled = verb.IsDisabled(user, component);
|
||||||
|
var button = new Button
|
||||||
|
{
|
||||||
|
Text = verb.GetText(user, component),
|
||||||
|
Disabled = disabled
|
||||||
|
};
|
||||||
|
if (!disabled)
|
||||||
|
{
|
||||||
|
button.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
_closeContextMenu();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
verb.Activate(user, component);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", verb, entity, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.Add(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttons.Count > 0)
|
||||||
|
{
|
||||||
|
buttons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal));
|
||||||
|
|
||||||
|
foreach (var button in buttons)
|
||||||
|
{
|
||||||
|
vBox.AddChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var panel = new PanelContainer();
|
||||||
|
panel.AddChild(new Label {Text = "No verbs!"});
|
||||||
|
vBox.AddChild(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPopup.Size = vBox.CombinedMinimumSize;
|
||||||
|
|
||||||
|
// If we're at the bottom of the window and the menu would go below the bottom of the window,
|
||||||
|
// shift it up so it extends UP.
|
||||||
|
var bottomCoord = vBox.CombinedMinimumSize.Y + _currentPopup.Position.Y;
|
||||||
|
if (bottomCoord > _userInterfaceManager.StateRoot.Size.Y)
|
||||||
|
{
|
||||||
|
_currentPopup.Position = _currentPopup.Position - new Vector2(0, vBox.CombinedMinimumSize.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _closeContextMenu()
|
||||||
|
{
|
||||||
|
_currentPopup?.Dispose();
|
||||||
|
_currentPopup = null;
|
||||||
|
_currentEntity = EntityUid.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEntity GetUserEntity()
|
||||||
|
{
|
||||||
|
return _playerManager.LocalPlayer.ControlledEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ namespace Content.Client.Input
|
|||||||
human.AddFunction(ContentKeyFunctions.UseItemInHand);
|
human.AddFunction(ContentKeyFunctions.UseItemInHand);
|
||||||
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
||||||
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
||||||
|
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||||
|
|
||||||
var ghost = contexts.New("ghost", "common");
|
var ghost = contexts.New("ghost", "common");
|
||||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||||
@@ -27,6 +28,7 @@ namespace Content.Client.Input
|
|||||||
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
|
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
|
||||||
ghost.AddFunction(EngineKeyFunctions.MoveRight);
|
ghost.AddFunction(EngineKeyFunctions.MoveRight);
|
||||||
ghost.AddFunction(EngineKeyFunctions.Run);
|
ghost.AddFunction(EngineKeyFunctions.Run);
|
||||||
|
ghost.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ namespace Content.Client.Interfaces.GameObjects
|
|||||||
string ActiveIndex { get; }
|
string ActiveIndex { get; }
|
||||||
|
|
||||||
void SendChangeHand(string index);
|
void SendChangeHand(string index);
|
||||||
|
void AttackByInHand(string index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Client.GameObjects;
|
using Content.Client.GameObjects;
|
||||||
|
using Content.Client.GameObjects.EntitySystems;
|
||||||
using Content.Client.Interfaces.GameObjects;
|
using Content.Client.Interfaces.GameObjects;
|
||||||
using SS14.Client.GameObjects;
|
using SS14.Client.GameObjects;
|
||||||
using SS14.Client.Graphics;
|
using SS14.Client.Graphics;
|
||||||
@@ -14,6 +15,7 @@ using SS14.Client.UserInterface.Controls;
|
|||||||
using SS14.Shared.Interfaces.GameObjects;
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
using SS14.Shared.IoC;
|
using SS14.Shared.IoC;
|
||||||
using SS14.Shared.Log;
|
using SS14.Shared.Log;
|
||||||
|
using SS14.Shared.Map;
|
||||||
using SS14.Shared.Maths;
|
using SS14.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Client.UserInterface
|
namespace Content.Client.UserInterface
|
||||||
@@ -21,7 +23,9 @@ namespace Content.Client.UserInterface
|
|||||||
public class HandsGui : Control
|
public class HandsGui : Control
|
||||||
{
|
{
|
||||||
private static readonly Color _inactiveColor = new Color(90, 90, 90);
|
private static readonly Color _inactiveColor = new Color(90, 90, 90);
|
||||||
|
|
||||||
private const int BOX_SPACING = 1;
|
private const int BOX_SPACING = 1;
|
||||||
|
|
||||||
// The boxes are square so that's both width and height.
|
// The boxes are square so that's both width and height.
|
||||||
private const int BOX_SIZE = 50;
|
private const int BOX_SIZE = 50;
|
||||||
|
|
||||||
@@ -123,6 +127,7 @@ namespace Content.Client.UserInterface
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateDraw();
|
UpdateDraw();
|
||||||
|
|
||||||
if (!TryGetHands(out IHandsComponent hands))
|
if (!TryGetHands(out IHandsComponent hands))
|
||||||
@@ -142,8 +147,8 @@ namespace Content.Client.UserInterface
|
|||||||
LeftHand.MirrorHandle?.Dispose();
|
LeftHand.MirrorHandle?.Dispose();
|
||||||
LeftHand.MirrorHandle = GetSpriteMirror(left);
|
LeftHand.MirrorHandle = GetSpriteMirror(left);
|
||||||
LeftHand.MirrorHandle.AttachToControl(this);
|
LeftHand.MirrorHandle.AttachToControl(this);
|
||||||
LeftHand.MirrorHandle.Offset = new Vector2(handL.Left + (int)(handL.Width / 2f),
|
LeftHand.MirrorHandle.Offset = new Vector2(handL.Left + (int) (handL.Width / 2f),
|
||||||
handL.Top + (int)(handL.Height / 2f));
|
handL.Top + (int) (handL.Height / 2f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -161,8 +166,8 @@ namespace Content.Client.UserInterface
|
|||||||
RightHand.MirrorHandle?.Dispose();
|
RightHand.MirrorHandle?.Dispose();
|
||||||
RightHand.MirrorHandle = GetSpriteMirror(right);
|
RightHand.MirrorHandle = GetSpriteMirror(right);
|
||||||
RightHand.MirrorHandle.AttachToControl(this);
|
RightHand.MirrorHandle.AttachToControl(this);
|
||||||
RightHand.MirrorHandle.Offset = new Vector2(handR.Left + (int)(handR.Width / 2f),
|
RightHand.MirrorHandle.Offset = new Vector2(handR.Left + (int) (handR.Width / 2f),
|
||||||
handR.Top + (int)(handR.Height / 2f));
|
handR.Top + (int) (handR.Height / 2f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -187,40 +192,79 @@ namespace Content.Client.UserInterface
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
//Todo: remove hands interface, so weird
|
//Todo: remove hands interface, so weird
|
||||||
((HandsComponent)hands).UseActiveHand();
|
((HandsComponent) hands).UseActiveHand();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AttackByInHand(string hand)
|
||||||
|
{
|
||||||
|
if (!TryGetHands(out var hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hands.AttackByInHand(hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool HasPoint(Vector2 point)
|
protected override bool HasPoint(Vector2 point)
|
||||||
{
|
{
|
||||||
return handL.Contains((Vector2i)point) || handR.Contains((Vector2i)point);
|
return handL.Contains((Vector2i) point) || handR.Contains((Vector2i) point);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void MouseDown(GUIMouseButtonEventArgs args)
|
protected override void MouseDown(GUIMouseButtonEventArgs args)
|
||||||
{
|
{
|
||||||
base.MouseDown(args);
|
base.MouseDown(args);
|
||||||
|
|
||||||
var lefthandcontains = handL.Contains((Vector2i)args.RelativePosition);
|
var leftHandContains = handL.Contains((Vector2i) args.RelativePosition);
|
||||||
var righthandcontains = handR.Contains((Vector2i)args.RelativePosition);
|
var rightHandContains = handR.Contains((Vector2i) args.RelativePosition);
|
||||||
|
|
||||||
|
string handIndex;
|
||||||
|
if (leftHandContains)
|
||||||
|
{
|
||||||
|
handIndex = "left";
|
||||||
|
}
|
||||||
|
else if (rightHandContains)
|
||||||
|
{
|
||||||
|
handIndex = "right";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Button == Mouse.Button.Left)
|
if (args.Button == Mouse.Button.Left)
|
||||||
{
|
{
|
||||||
if (!TryGetHands(out IHandsComponent hands))
|
if (!TryGetHands(out var hands))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((hands.ActiveIndex == "left" && lefthandcontains)
|
|
||||||
|| (hands.ActiveIndex == "right" && righthandcontains))
|
if (hands.ActiveIndex == handIndex)
|
||||||
|
{
|
||||||
UseActiveHand();
|
UseActiveHand();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AttackByInHand(handIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (args.Button == Mouse.Button.Middle)
|
||||||
|
{
|
||||||
|
SendSwitchHandTo(handIndex);
|
||||||
|
}
|
||||||
|
|
||||||
else if (args.Button == Mouse.Button.Right)
|
else if (args.Button == Mouse.Button.Right)
|
||||||
{
|
{
|
||||||
if (lefthandcontains)
|
if (!TryGetHands(out var hands))
|
||||||
{
|
{
|
||||||
SendSwitchHandTo("left");
|
return;
|
||||||
}
|
}
|
||||||
if (righthandcontains)
|
|
||||||
|
var entity = hands.GetEntity(handIndex);
|
||||||
|
if (entity == null)
|
||||||
{
|
{
|
||||||
SendSwitchHandTo("right");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var esm = IoCManager.Resolve<IEntitySystemManager>();
|
||||||
|
esm.GetEntitySystem<VerbSystem>().OpenContextMenu(entity, new ScreenCoordinates(args.GlobalPosition));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,13 +274,14 @@ namespace Content.Client.UserInterface
|
|||||||
{
|
{
|
||||||
return component.CreateProxy();
|
return component.CreateProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct UiHandInfo
|
private struct UiHandInfo
|
||||||
{
|
{
|
||||||
public IEntity Entity { get; set; }
|
public IEntity Entity { get; set; }
|
||||||
public ISpriteProxy MirrorHandle { get; set; }
|
public ISpriteProxy MirrorHandle { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
<Compile Include="GameObjects\EntitySystems\PowerApcSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\PowerApcSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\PowerSmesSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\PowerSmesSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\PowerSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\PowerSystem.cs" />
|
||||||
|
<Compile Include="GameObjects\EntitySystems\VerbSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\StorageSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\StorageSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\WelderSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\WelderSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\TemperatureSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\TemperatureSystem.cs" />
|
||||||
@@ -168,4 +169,4 @@
|
|||||||
<Compile Include="GameObjects\Components\Construction\ConstructorComponent.cs" />
|
<Compile Include="GameObjects\Components\Construction\ConstructorComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Construction\ConstructionComponent.cs" />
|
<Compile Include="GameObjects\Components\Construction\ConstructionComponent.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Server.GameObjects.EntitySystems;
|
|||||||
using Content.Server.Interfaces.GameObjects;
|
using Content.Server.Interfaces.GameObjects;
|
||||||
using Content.Shared.GameObjects;
|
using Content.Shared.GameObjects;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using SS14.Server.GameObjects;
|
using SS14.Server.GameObjects;
|
||||||
using SS14.Server.GameObjects.Components.Container;
|
using SS14.Server.GameObjects.Components.Container;
|
||||||
using SS14.Server.Interfaces.Player;
|
using SS14.Server.Interfaces.Player;
|
||||||
@@ -13,8 +14,10 @@ using SS14.Shared.Interfaces.GameObjects;
|
|||||||
using SS14.Shared.Interfaces.GameObjects.Components;
|
using SS14.Shared.Interfaces.GameObjects.Components;
|
||||||
using SS14.Shared.Interfaces.Network;
|
using SS14.Shared.Interfaces.Network;
|
||||||
using SS14.Shared.IoC;
|
using SS14.Shared.IoC;
|
||||||
|
using SS14.Shared.Log;
|
||||||
using SS14.Shared.Map;
|
using SS14.Shared.Map;
|
||||||
using SS14.Shared.Serialization;
|
using SS14.Shared.Serialization;
|
||||||
|
using SS14.Shared.Utility;
|
||||||
using SS14.Shared.ViewVariables;
|
using SS14.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects
|
namespace Content.Server.GameObjects
|
||||||
@@ -39,8 +42,8 @@ namespace Content.Server.GameObjects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<string, ContainerSlot> hands = new Dictionary<string, ContainerSlot>();
|
[ViewVariables] private Dictionary<string, ContainerSlot> hands = new Dictionary<string, ContainerSlot>();
|
||||||
private List<string> orderedHands = new List<string>();
|
[ViewVariables] private List<string> orderedHands = new List<string>();
|
||||||
|
|
||||||
// Mostly arbitrary.
|
// Mostly arbitrary.
|
||||||
public const float PICKUP_RANGE = 2;
|
public const float PICKUP_RANGE = 2;
|
||||||
@@ -74,7 +77,7 @@ namespace Content.Server.GameObjects
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void RemoveHandEntity(IEntity entity)
|
public void RemoveHandEntity(IEntity entity)
|
||||||
{
|
{
|
||||||
if(entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var slot in hands.Values)
|
foreach (var slot in hands.Values)
|
||||||
@@ -155,13 +158,20 @@ namespace Content.Server.GameObjects
|
|||||||
return slot.CanInsert(item.Owner);
|
return slot.CanInsert(item.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public string FindHand(IEntity entity)
|
||||||
/// Drops the item in a slot.
|
{
|
||||||
/// </summary>
|
foreach (var (index, slot) in hands)
|
||||||
/// <param name="slot">The slot to drop the item from.</param>
|
{
|
||||||
/// <param name="coords"></param>
|
if (slot.ContainedEntity == entity)
|
||||||
/// <returns>True if an item was dropped, false otherwise.</returns>
|
{
|
||||||
public bool Drop(string slot, GridLocalCoordinates? coords)
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Drop(string slot, GridLocalCoordinates coords)
|
||||||
{
|
{
|
||||||
if (!CanDrop(slot))
|
if (!CanDrop(slot))
|
||||||
{
|
{
|
||||||
@@ -178,14 +188,119 @@ namespace Content.Server.GameObjects
|
|||||||
item.RemovedFromSlot();
|
item.RemovedFromSlot();
|
||||||
|
|
||||||
// TODO: The item should be dropped to the container our owner is in, if any.
|
// TODO: The item should be dropped to the container our owner is in, if any.
|
||||||
var itemTransform = item.Owner.GetComponent<ITransformComponent>();
|
item.Owner.Transform.LocalPosition = coords;
|
||||||
|
|
||||||
itemTransform.LocalPosition = coords ?? Owner.GetComponent<ITransformComponent>().LocalPosition;
|
|
||||||
|
|
||||||
Dirty();
|
Dirty();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Drop(IEntity entity, GridLocalCoordinates coords)
|
||||||
|
{
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
var slot = FindHand(entity);
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Drop(slot, coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Drop(string slot)
|
||||||
|
{
|
||||||
|
if (!CanDrop(slot))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inventorySlot = hands[slot];
|
||||||
|
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
|
||||||
|
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.RemovedFromSlot();
|
||||||
|
|
||||||
|
// TODO: The item should be dropped to the container our owner is in, if any.
|
||||||
|
item.Owner.Transform.LocalPosition = Owner.Transform.LocalPosition;
|
||||||
|
|
||||||
|
Dirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool Drop(IEntity entity)
|
||||||
|
{
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
var slot = FindHand(entity);
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Drop(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Drop(string slot, BaseContainer targetContainer)
|
||||||
|
{
|
||||||
|
if (!CanDrop(slot))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inventorySlot = hands[slot];
|
||||||
|
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
|
||||||
|
if (!inventorySlot.CanRemove(inventorySlot.ContainedEntity))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetContainer.CanInsert(inventorySlot.ContainedEntity))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
item.RemovedFromSlot();
|
||||||
|
|
||||||
|
if (!targetContainer.Insert(item.Owner))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Drop(IEntity entity, BaseContainer targetContainer)
|
||||||
|
{
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
var slot = FindHand(entity);
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Drop(slot, targetContainer);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether an item can be dropped from the specified slot.
|
/// Checks whether an item can be dropped from the specified slot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -212,10 +327,12 @@ namespace Content.Server.GameObjects
|
|||||||
{
|
{
|
||||||
orderedHands.Add(index);
|
orderedHands.Add(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ActiveIndex == null)
|
if (ActiveIndex == null)
|
||||||
{
|
{
|
||||||
ActiveIndex = index;
|
ActiveIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +358,7 @@ namespace Content.Server.GameObjects
|
|||||||
activeIndex = orderedHands[0];
|
activeIndex = orderedHands[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +382,7 @@ namespace Content.Server.GameObjects
|
|||||||
dict[hand.Key] = hand.Value.ContainedEntity.Uid;
|
dict[hand.Key] = hand.Value.ContainedEntity.Uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HandsComponentState(dict, ActiveIndex);
|
return new HandsComponentState(dict, ActiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,36 +407,61 @@ namespace Content.Server.GameObjects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||||
|
IComponent component = null)
|
||||||
{
|
{
|
||||||
base.HandleMessage(message, netChannel, component);
|
base.HandleMessage(message, netChannel, component);
|
||||||
|
|
||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
case ClientChangedHandMsg msg:
|
case ClientChangedHandMsg msg:
|
||||||
{
|
{
|
||||||
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||||
var session = playerMan.GetSessionByChannel(netChannel);
|
var session = playerMan.GetSessionByChannel(netChannel);
|
||||||
var playerentity = session.AttachedEntity;
|
var playerEntity = session.AttachedEntity;
|
||||||
|
|
||||||
if (playerentity == Owner && HasHand(msg.Index))
|
if (playerEntity == Owner && HasHand(msg.Index))
|
||||||
ActiveIndex = msg.Index;
|
ActiveIndex = msg.Index;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ClientAttackByInHandMsg msg:
|
||||||
|
{
|
||||||
|
if (!hands.TryGetValue(msg.Index, out var slot))
|
||||||
|
{
|
||||||
|
Logger.WarningS("go.comp.hands", "Got a ClientAttackByInHandMsg with invalid hand index '{0}'",
|
||||||
|
msg.Index);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||||
|
var session = playerMan.GetSessionByChannel(netChannel);
|
||||||
|
var playerEntity = session.AttachedEntity;
|
||||||
|
var used = GetActiveHand?.Owner;
|
||||||
|
|
||||||
|
if (playerEntity == Owner && used != null)
|
||||||
|
{
|
||||||
|
InteractionSystem.Interaction(Owner, used, slot.ContainedEntity,
|
||||||
|
GridLocalCoordinates.Nullspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case ActivateInhandMsg msg:
|
case ActivateInhandMsg msg:
|
||||||
{
|
{
|
||||||
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||||
var session = playerMan.GetSessionByChannel(netChannel);
|
var session = playerMan.GetSessionByChannel(netChannel);
|
||||||
var playerentity = session.AttachedEntity;
|
var playerEntity = session.AttachedEntity;
|
||||||
var used = GetActiveHand?.Owner;
|
var used = GetActiveHand?.Owner;
|
||||||
|
|
||||||
if (playerentity == Owner && used != null)
|
if (playerEntity == Owner && used != null)
|
||||||
{
|
{
|
||||||
InteractionSystem.TryUseInteraction(Owner, used);
|
InteractionSystem.TryUseInteraction(Owner, used);
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
using Content.Server.GameObjects.EntitySystems;
|
using Content.Server.GameObjects.Components.Power;
|
||||||
using Content.Server.GameObjects.Components.Power;
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Interfaces.GameObjects;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
using SS14.Server.GameObjects;
|
using SS14.Server.GameObjects;
|
||||||
using SS14.Server.GameObjects.Components.Container;
|
using SS14.Server.GameObjects.Components.Container;
|
||||||
using SS14.Shared.Enums;
|
using SS14.Shared.Enums;
|
||||||
using SS14.Shared.Interfaces.GameObjects;
|
|
||||||
using SS14.Shared.GameObjects;
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
using SS14.Shared.ViewVariables;
|
using SS14.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Interactable
|
namespace Content.Server.GameObjects.Components.Interactable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component that represents a handheld lightsource which can be toggled on and off.
|
/// Component that represents a handheld lightsource which can be toggled on and off.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class HandheldLightComponent : Component, IUse, IExamine
|
internal class HandheldLightComponent : Component, IUse, IExamine, IAttackby
|
||||||
{
|
{
|
||||||
|
public const float Wattage = 10;
|
||||||
|
[ViewVariables] private ContainerSlot _cellContainer;
|
||||||
private PointLightComponent _pointLight;
|
private PointLightComponent _pointLight;
|
||||||
private SpriteComponent _spriteComponent;
|
private SpriteComponent _spriteComponent;
|
||||||
[ViewVariables] private ContainerSlot _cellContainer;
|
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
private PowerCellComponent Cell
|
private PowerCellComponent Cell
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_cellContainer.ContainedEntity == null)
|
if (_cellContainer.ContainedEntity == null) return null;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cellContainer.ContainedEntity.TryGetComponent(out PowerCellComponent cell);
|
_cellContainer.ContainedEntity.TryGetComponent(out PowerCellComponent cell);
|
||||||
return cell;
|
return cell;
|
||||||
@@ -35,12 +36,33 @@ namespace Content.Server.GameObjects.Components.Interactable
|
|||||||
public override string Name => "HandheldLight";
|
public override string Name => "HandheldLight";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Status of light, whether or not it is emitting light.
|
/// Status of light, whether or not it is emitting light.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public bool Activated { get; private set; } = false;
|
public bool Activated { get; private set; }
|
||||||
|
|
||||||
public const float Wattage = 10;
|
bool IAttackby.Attackby(IEntity user, IEntity attackwith)
|
||||||
|
{
|
||||||
|
if (!attackwith.HasComponent<PowerCellComponent>()) return false;
|
||||||
|
|
||||||
|
if (Cell != null) return false;
|
||||||
|
|
||||||
|
user.GetComponent<IHandsComponent>().Drop(attackwith, _cellContainer);
|
||||||
|
|
||||||
|
return _cellContainer.Insert(attackwith);
|
||||||
|
}
|
||||||
|
|
||||||
|
string IExamine.Examine()
|
||||||
|
{
|
||||||
|
if (Activated) return "The light is currently on.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IUse.UseEntity(IEntity user)
|
||||||
|
{
|
||||||
|
return ToggleStatus();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -58,13 +80,8 @@ namespace Content.Server.GameObjects.Components.Interactable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IUse.UseEntity(IEntity user)
|
|
||||||
{
|
|
||||||
return ToggleStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Illuminates the light if it is not active, extinguishes it if it is active.
|
/// Illuminates the light if it is not active, extinguishes it if it is active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the light's status was toggled, false otherwise.</returns>
|
/// <returns>True if the light's status was toggled, false otherwise.</returns>
|
||||||
public bool ToggleStatus()
|
public bool ToggleStatus()
|
||||||
@@ -90,10 +107,7 @@ namespace Content.Server.GameObjects.Components.Interactable
|
|||||||
|
|
||||||
public void TurnOff()
|
public void TurnOff()
|
||||||
{
|
{
|
||||||
if (!Activated)
|
if (!Activated) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_spriteComponent.LayerSetState(0, "lantern_off");
|
_spriteComponent.LayerSetState(0, "lantern_off");
|
||||||
_pointLight.State = LightState.Off;
|
_pointLight.State = LightState.Off;
|
||||||
@@ -102,50 +116,57 @@ namespace Content.Server.GameObjects.Components.Interactable
|
|||||||
|
|
||||||
public void TurnOn()
|
public void TurnOn()
|
||||||
{
|
{
|
||||||
if (Activated)
|
if (Activated) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cell = Cell;
|
var cell = Cell;
|
||||||
if (cell == null)
|
if (cell == null) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To prevent having to worry about frame time in here.
|
// To prevent having to worry about frame time in here.
|
||||||
// Let's just say you need a whole second of charge before you can turn it on.
|
// Let's just say you need a whole second of charge before you can turn it on.
|
||||||
// Simple enough.
|
// Simple enough.
|
||||||
if (cell.AvailableCharge(1) < Wattage)
|
if (cell.AvailableCharge(1) < Wattage) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_spriteComponent.LayerSetState(0, "lantern_on");
|
_spriteComponent.LayerSetState(0, "lantern_on");
|
||||||
_pointLight.State = LightState.On;
|
_pointLight.State = LightState.On;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IExamine.Examine()
|
|
||||||
{
|
|
||||||
if (Activated)
|
|
||||||
{
|
|
||||||
return "The light is currently on.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnUpdate(float frameTime)
|
public void OnUpdate(float frameTime)
|
||||||
{
|
{
|
||||||
if (!Activated)
|
if (!Activated) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cell = Cell;
|
var cell = Cell;
|
||||||
if (cell == null || !cell.TryDeductWattage(Wattage, frameTime))
|
if (cell == null || !cell.TryDeductWattage(Wattage, frameTime)) TurnOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EjectCell(IEntity user)
|
||||||
|
{
|
||||||
|
if (Cell == null) return;
|
||||||
|
|
||||||
|
var cell = Cell;
|
||||||
|
|
||||||
|
if (!_cellContainer.Remove(cell.Owner)) return;
|
||||||
|
|
||||||
|
if (!user.TryGetComponent(out HandsComponent hands)
|
||||||
|
|| !hands.PutInHand(cell.Owner.GetComponent<ItemComponent>()))
|
||||||
|
cell.Owner.Transform.LocalPosition = user.Transform.LocalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Verb]
|
||||||
|
public sealed class EjectCellVerb : Verb<HandheldLightComponent>
|
||||||
|
{
|
||||||
|
protected override string GetText(IEntity user, HandheldLightComponent component)
|
||||||
{
|
{
|
||||||
TurnOff();
|
return component.Cell == null ? "Eject cell (cell missing)" : "Eject cell";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsDisabled(IEntity user, HandheldLightComponent component)
|
||||||
|
{
|
||||||
|
return component.Cell == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Activate(IEntity user, HandheldLightComponent component)
|
||||||
|
{
|
||||||
|
component.EjectCell(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
input.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem));
|
input.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem));
|
||||||
input.BindMap.BindFunction(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem));
|
input.BindMap.BindFunction(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
@@ -118,13 +118,14 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
|
|
||||||
var transform = ent.Transform;
|
var transform = ent.Transform;
|
||||||
|
|
||||||
GridLocalCoordinates? dropPos = null;
|
|
||||||
if (transform.LocalPosition.InRange(coords, InteractionSystem.INTERACTION_RANGE))
|
if (transform.LocalPosition.InRange(coords, InteractionSystem.INTERACTION_RANGE))
|
||||||
{
|
{
|
||||||
dropPos = coords;
|
handsComp.Drop(handsComp.ActiveIndex, coords);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handsComp.Drop(handsComp.ActiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
handsComp.Drop(handsComp.ActiveIndex, dropPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleActivateItem(ICommonSession session)
|
private static void HandleActivateItem(ICommonSession session)
|
||||||
@@ -160,7 +161,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
stackComp.Use(1);
|
stackComp.Use(1);
|
||||||
throwEnt = throwEnt.EntityManager.ForceSpawnEntityAt(throwEnt.Prototype.ID, plyEnt.Transform.LocalPosition);
|
throwEnt = throwEnt.EntityManager.ForceSpawnEntityAt(throwEnt.Prototype.ID, plyEnt.Transform.LocalPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!throwEnt.TryGetComponent(out CollidableComponent colComp))
|
if (!throwEnt.TryGetComponent(out CollidableComponent colComp))
|
||||||
{
|
{
|
||||||
colComp = throwEnt.AddComponent<CollidableComponent>();
|
colComp = throwEnt.AddComponent<CollidableComponent>();
|
||||||
@@ -180,7 +181,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
{
|
{
|
||||||
projComp = throwEnt.AddComponent<ThrownItemComponent>();
|
projComp = throwEnt.AddComponent<ThrownItemComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
projComp.IgnoreEntity(plyEnt);
|
projComp.IgnoreEntity(plyEnt);
|
||||||
|
|
||||||
var transform = plyEnt.Transform;
|
var transform = plyEnt.Transform;
|
||||||
|
|||||||
113
Content.Server/GameObjects/EntitySystems/VerbSystem.cs
Normal file
113
Content.Server/GameObjects/EntitySystems/VerbSystem.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.EntitySystemMessages;
|
||||||
|
using SS14.Server.Interfaces.Player;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.GameObjects.Systems;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
using SS14.Shared.Interfaces.Network;
|
||||||
|
using SS14.Shared.IoC;
|
||||||
|
using static Content.Shared.GameObjects.EntitySystemMessages.VerbSystemMessages;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
public class VerbSystem : EntitySystem
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RegisterMessageTypes()
|
||||||
|
{
|
||||||
|
base.RegisterMessageTypes();
|
||||||
|
|
||||||
|
RegisterMessageType<RequestVerbsMessage>();
|
||||||
|
RegisterMessageType<UseVerbMessage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
|
||||||
|
{
|
||||||
|
base.HandleNetMessage(channel, message);
|
||||||
|
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case RequestVerbsMessage req:
|
||||||
|
{
|
||||||
|
if (!_entityManager.TryGetEntity(req.EntityUid, out var entity))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var session = _playerManager.GetSessionByChannel(channel);
|
||||||
|
var userEntity = session.AttachedEntity;
|
||||||
|
|
||||||
|
var data = new List<VerbsResponseMessage.VerbData>();
|
||||||
|
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
|
||||||
|
{
|
||||||
|
if (verb.RequireInteractionRange)
|
||||||
|
{
|
||||||
|
var distanceSquared = (userEntity.Transform.WorldPosition - entity.Transform.WorldPosition)
|
||||||
|
.LengthSquared;
|
||||||
|
if (distanceSquared > Verb.InteractionRangeSquared)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: These keys being giant strings is inefficient as hell.
|
||||||
|
data.Add(new VerbsResponseMessage.VerbData(verb.GetText(userEntity, component),
|
||||||
|
$"{component.GetType()}:{verb.GetType()}",
|
||||||
|
!verb.IsDisabled(userEntity, component)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = new VerbsResponseMessage(data, req.EntityUid);
|
||||||
|
RaiseNetworkEvent(response, channel);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case UseVerbMessage use:
|
||||||
|
{
|
||||||
|
if (!_entityManager.TryGetEntity(use.EntityUid, out var entity))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var session = _playerManager.GetSessionByChannel(channel);
|
||||||
|
var userEntity = session.AttachedEntity;
|
||||||
|
|
||||||
|
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
|
||||||
|
{
|
||||||
|
if ($"{component.GetType()}:{verb.GetType()}" != use.VerbKey)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verb.RequireInteractionRange)
|
||||||
|
{
|
||||||
|
var distanceSquared = (userEntity.Transform.WorldPosition - entity.Transform.WorldPosition)
|
||||||
|
.LengthSquared;
|
||||||
|
if (distanceSquared > Verb.InteractionRangeSquared)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verb.Activate(userEntity, component);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using Content.Server.GameObjects;
|
using System;
|
||||||
|
using Content.Server.GameObjects;
|
||||||
using SS14.Shared.Interfaces.GameObjects;
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using SS14.Server.GameObjects.Components.Container;
|
||||||
using SS14.Shared.Map;
|
using SS14.Shared.Map;
|
||||||
|
|
||||||
namespace Content.Server.Interfaces.GameObjects
|
namespace Content.Server.Interfaces.GameObjects
|
||||||
@@ -69,12 +71,96 @@ namespace Content.Server.Interfaces.GameObjects
|
|||||||
bool CanPutInHand(ItemComponent item, string index);
|
bool CanPutInHand(ItemComponent item, string index);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drops an item on the ground, removing it from the hand.
|
/// Finds the hand slot holding the specified entity, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">The hand to drop from.</param>
|
/// <param name="entity">
|
||||||
|
/// The entity to look for in our hands.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The index of the hand slot if the entity is indeed held, <see langword="null" /> otherwise.
|
||||||
|
/// </returns>
|
||||||
|
string FindHand(IEntity entity);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drops the item contained in the slot to the same position as our entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">The slot of which to drop to drop the item.</param>
|
||||||
|
/// <returns>True on success, false if something blocked the drop.</returns>
|
||||||
|
bool Drop(string slot);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drops an item held by one of our hand slots to the same position as our owning entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity">The item to drop.</param>
|
||||||
|
/// <returns>True on success, false if something blocked the drop.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Thrown if <see cref="entity"/> is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown if <see cref="entity"/> is not actually held in any hand.
|
||||||
|
/// </exception>
|
||||||
|
bool Drop(IEntity entity);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drops the item in a slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">The slot to drop the item from.</param>
|
||||||
/// <param name="coords"></param>
|
/// <param name="coords"></param>
|
||||||
/// <returns>True if an item was successfully dropped, false otherwise.</returns>
|
/// <returns>True if an item was dropped, false otherwise.</returns>
|
||||||
bool Drop(string index, GridLocalCoordinates? coords);
|
bool Drop(string slot, GridLocalCoordinates coords);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drop the specified entity in our hands to a certain position.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// There are no checks whether or not the user is within interaction range of the drop location
|
||||||
|
/// or whether the drop location is occupied.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="entity">The entity to drop, must be held in one of the hands.</param>
|
||||||
|
/// <param name="coords">The coordinates to drop the entity at.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if the drop succeeded,
|
||||||
|
/// false if it failed (due to failing to eject from our hand slot, etc...)
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Thrown if <see cref="entity"/> is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown if <see cref="entity"/> is not actually held in any hand.
|
||||||
|
/// </exception>
|
||||||
|
bool Drop(IEntity entity, GridLocalCoordinates coords);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drop the item contained in a slot into another container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">The slot of which to drop the entity.</param>
|
||||||
|
/// <param name="targetContainer">The container to drop into.</param>
|
||||||
|
/// <returns>True on success, false if something was blocked (insertion or removal).</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">
|
||||||
|
/// Thrown if dry-run checks reported OK to remove and insert,
|
||||||
|
/// but practical remove or insert returned false anyways.
|
||||||
|
/// This is an edge-case that is currently unhandled.
|
||||||
|
/// </exception>
|
||||||
|
bool Drop(string slot, BaseContainer targetContainer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drops an item in one of the hands into a container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity">The item to drop.</param>
|
||||||
|
/// <param name="targetContainer">The container to drop into.</param>
|
||||||
|
/// <returns>True on success, false if something was blocked (insertion or removal).</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">
|
||||||
|
/// Thrown if dry-run checks reported OK to remove and insert,
|
||||||
|
/// but practical remove or insert returned false anyways.
|
||||||
|
/// This is an edge-case that is currently unhandled.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Thrown if <see cref="entity"/> is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown if <see cref="entity"/> is not actually held in any hand.
|
||||||
|
/// </exception>
|
||||||
|
bool Drop(IEntity entity, BaseContainer targetContainer);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether the item in the specified hand can be dropped.
|
/// Checks whether the item in the specified hand can be dropped.
|
||||||
|
|||||||
@@ -70,7 +70,9 @@
|
|||||||
<Compile Include="GameObjects\Components\Power\SharedPowerCellComponent.cs" />
|
<Compile Include="GameObjects\Components\Power\SharedPowerCellComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Storage\SharedStorageComponent.cs" />
|
<Compile Include="GameObjects\Components\Storage\SharedStorageComponent.cs" />
|
||||||
<Compile Include="GameObjects\ContentNetIDs.cs" />
|
<Compile Include="GameObjects\ContentNetIDs.cs" />
|
||||||
|
<Compile Include="GameObjects\EntitySystemMessages\VerbSystemMessages.cs" />
|
||||||
<Compile Include="GameObjects\PhysicalConstants.cs" />
|
<Compile Include="GameObjects\PhysicalConstants.cs" />
|
||||||
|
<Compile Include="GameObjects\Verb.cs" />
|
||||||
<Compile Include="Physics\CollisionGroup.cs" />
|
<Compile Include="Physics\CollisionGroup.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="GameObjects\Components\Items\SharedHandsComponent.cs" />
|
<Compile Include="GameObjects\Components\Items\SharedHandsComponent.cs" />
|
||||||
|
|||||||
@@ -37,4 +37,16 @@ namespace Content.Shared.GameObjects
|
|||||||
Directed = true;
|
Directed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class ClientAttackByInHandMsg : ComponentMessage
|
||||||
|
{
|
||||||
|
public string Index { get; }
|
||||||
|
|
||||||
|
public ClientAttackByInHandMsg(string index)
|
||||||
|
{
|
||||||
|
Directed = true;
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.EntitySystemMessages
|
||||||
|
{
|
||||||
|
public static class VerbSystemMessages
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class RequestVerbsMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
public readonly EntityUid EntityUid;
|
||||||
|
|
||||||
|
public RequestVerbsMessage(EntityUid entityUid)
|
||||||
|
{
|
||||||
|
EntityUid = entityUid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class VerbsResponseMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
public readonly List<VerbData> Verbs;
|
||||||
|
public readonly EntityUid Entity;
|
||||||
|
|
||||||
|
public VerbsResponseMessage(List<VerbData> verbs, EntityUid entity)
|
||||||
|
{
|
||||||
|
Verbs = verbs;
|
||||||
|
Entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public readonly struct VerbData
|
||||||
|
{
|
||||||
|
public readonly string Text;
|
||||||
|
public readonly string Key;
|
||||||
|
public readonly bool Available;
|
||||||
|
|
||||||
|
public VerbData(string text, string key, bool available)
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
|
Key = key;
|
||||||
|
Available = available;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class UseVerbMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
public readonly EntityUid EntityUid;
|
||||||
|
public readonly string VerbKey;
|
||||||
|
|
||||||
|
public UseVerbMessage(EntityUid entityUid, string verbKey)
|
||||||
|
{
|
||||||
|
EntityUid = entityUid;
|
||||||
|
VerbKey = verbKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
132
Content.Shared/GameObjects/Verb.cs
Normal file
132
Content.Shared/GameObjects/Verb.cs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A verb is an action in the right click menu of an entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// To add a verb to an entity, define it as a nested class inside the owning component,
|
||||||
|
/// and mark it with <see cref="VerbAttribute"/>
|
||||||
|
/// </remarks>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public abstract class Verb
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If true, this verb requires the user to be inside within
|
||||||
|
/// <see cref="InteractionRange"/> meters from the entity on which this verb resides.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool RequireInteractionRange => true;
|
||||||
|
|
||||||
|
public const float InteractionRange = 2;
|
||||||
|
public const float InteractionRangeSquared = InteractionRange * InteractionRange;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the text string that will be shown to <paramref name="user"/> in the right click menu.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity of the user opening this menu.</param>
|
||||||
|
/// <param name="component">The component instance for which this verb is being loaded.</param>
|
||||||
|
/// <returns>The text string that is shown in the right click menu for this verb.</returns>
|
||||||
|
public abstract string GetText(IEntity user, IComponent component);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether this verb is "disabled" in the right click menu.
|
||||||
|
/// The verb is still visible in disabled state, but greyed out.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity of the user opening this menu.</param>
|
||||||
|
/// <param name="component">The component instance for which this verb is being loaded.</param>
|
||||||
|
/// <returns>True if the verb is disabled, false otherwise.</returns>
|
||||||
|
public abstract bool IsDisabled(IEntity user, IComponent component);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when this verb is activated from the right click menu.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity of the user opening this menu.</param>
|
||||||
|
/// <param name="component">The component instance for which this verb is being loaded.</param>
|
||||||
|
public abstract void Activate(IEntity user, IComponent component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// Sub class of <see cref="T:Content.Shared.GameObjects.Verb" /> that works on a specific type of component,
|
||||||
|
/// to reduce casting boiler plate for implementations.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of component that this verb will run on.</typeparam>
|
||||||
|
public abstract class Verb<T> : Verb where T : IComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the text string that will be shown to <paramref name="user"/> in the right click menu.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity of the user opening this menu.</param>
|
||||||
|
/// <param name="component">The component instance for which this verb is being loaded.</param>
|
||||||
|
/// <returns>The text string that is shown in the right click menu for this verb.</returns>
|
||||||
|
protected abstract string GetText(IEntity user, T component);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether this verb is "disabled" in the right click menu.
|
||||||
|
/// The verb is still visible in disabled state, but greyed out.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity of the user opening this menu.</param>
|
||||||
|
/// <param name="component">The component instance for which this verb is being loaded.</param>
|
||||||
|
/// <returns>True if the verb is disabled, false otherwise.</returns>
|
||||||
|
protected abstract bool IsDisabled(IEntity user, T component);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when this verb is activated from the right click menu.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The entity of the user opening this menu.</param>
|
||||||
|
/// <param name="component">The component instance for which this verb is being loaded.</param>
|
||||||
|
protected abstract void Activate(IEntity user, T component);
|
||||||
|
|
||||||
|
|
||||||
|
public sealed override string GetText(IEntity user, IComponent component)
|
||||||
|
{
|
||||||
|
return GetText(user, (T) component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed override bool IsDisabled(IEntity user, IComponent component)
|
||||||
|
{
|
||||||
|
return IsDisabled(user, (T) component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed override void Activate(IEntity user, IComponent component)
|
||||||
|
{
|
||||||
|
Activate(user, (T) component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This attribute should be used on <see cref="Verb"/> implementations nested inside component classes,
|
||||||
|
/// so that they're automatically detected.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public sealed class VerbAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VerbUtility
|
||||||
|
{
|
||||||
|
// TODO: This is a quick hack. Verb objects should absolutely be cached properly.
|
||||||
|
// This works for now though.
|
||||||
|
public static IEnumerable<(IComponent, Verb)> GetVerbs(IEntity entity)
|
||||||
|
{
|
||||||
|
foreach (var component in entity.GetAllComponents())
|
||||||
|
{
|
||||||
|
var type = component.GetType();
|
||||||
|
foreach (var nestedType in type.GetNestedTypes())
|
||||||
|
{
|
||||||
|
if (!typeof(Verb).IsAssignableFrom(nestedType) || nestedType.IsAbstract)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var verb = (Verb) Activator.CreateInstance(nestedType);
|
||||||
|
yield return (component, verb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,5 +13,6 @@ namespace Content.Shared.Input
|
|||||||
public static readonly BoundKeyFunction UseItemInHand = "UseItemInHand"; // use hand item on world entity
|
public static readonly BoundKeyFunction UseItemInHand = "UseItemInHand"; // use hand item on world entity
|
||||||
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity
|
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity
|
||||||
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
||||||
|
public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,6 @@ binds:
|
|||||||
key: MouseLeft
|
key: MouseLeft
|
||||||
mod1: Control
|
mod1: Control
|
||||||
type: state
|
type: state
|
||||||
|
- function: OpenContextMenu
|
||||||
|
key: MouseRight
|
||||||
|
type: state
|
||||||
|
|||||||
2
engine
2
engine
Submodule engine updated: 5ae665c3d2...69c0d409c8
Reference in New Issue
Block a user