Right click verbs work.
This commit is contained in:
Pieter-Jan Briers
2018-11-21 20:58:11 +01:00
committed by GitHub
parent 8038ebe37d
commit b0f212bad5
20 changed files with 982 additions and 112 deletions

View File

@@ -104,6 +104,7 @@
<Compile Include="GameObjects\EntitySystems\PowerApcSystem.cs" />
<Compile Include="GameObjects\EntitySystems\PowerSmesSystem.cs" />
<Compile Include="GameObjects\EntitySystems\PowerSystem.cs" />
<Compile Include="GameObjects\EntitySystems\VerbSystem.cs" />
<Compile Include="GameObjects\EntitySystems\StorageSystem.cs" />
<Compile Include="GameObjects\EntitySystems\WelderSystem.cs" />
<Compile Include="GameObjects\EntitySystems\TemperatureSystem.cs" />
@@ -168,4 +169,4 @@
<Compile Include="GameObjects\Components\Construction\ConstructorComponent.cs" />
<Compile Include="GameObjects\Components\Construction\ConstructionComponent.cs" />
</ItemGroup>
</Project>
</Project>

View File

@@ -4,6 +4,7 @@ using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
using Content.Shared.GameObjects;
using Content.Shared.Input;
using JetBrains.Annotations;
using SS14.Server.GameObjects;
using SS14.Server.GameObjects.Components.Container;
using SS14.Server.Interfaces.Player;
@@ -13,8 +14,10 @@ using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.GameObjects.Components;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Map;
using SS14.Shared.Serialization;
using SS14.Shared.Utility;
using SS14.Shared.ViewVariables;
namespace Content.Server.GameObjects
@@ -39,8 +42,8 @@ namespace Content.Server.GameObjects
}
}
private Dictionary<string, ContainerSlot> hands = new Dictionary<string, ContainerSlot>();
private List<string> orderedHands = new List<string>();
[ViewVariables] private Dictionary<string, ContainerSlot> hands = new Dictionary<string, ContainerSlot>();
[ViewVariables] private List<string> orderedHands = new List<string>();
// Mostly arbitrary.
public const float PICKUP_RANGE = 2;
@@ -74,7 +77,7 @@ namespace Content.Server.GameObjects
/// <inheritdoc />
public void RemoveHandEntity(IEntity entity)
{
if(entity == null)
if (entity == null)
return;
foreach (var slot in hands.Values)
@@ -155,13 +158,20 @@ namespace Content.Server.GameObjects
return slot.CanInsert(item.Owner);
}
/// <summary>
/// Drops the item in a slot.
/// </summary>
/// <param name="slot">The slot to drop the item from.</param>
/// <param name="coords"></param>
/// <returns>True if an item was dropped, false otherwise.</returns>
public bool Drop(string slot, GridLocalCoordinates? coords)
public string FindHand(IEntity entity)
{
foreach (var (index, slot) in hands)
{
if (slot.ContainedEntity == entity)
{
return index;
}
}
return null;
}
public bool Drop(string slot, GridLocalCoordinates coords)
{
if (!CanDrop(slot))
{
@@ -178,14 +188,119 @@ namespace Content.Server.GameObjects
item.RemovedFromSlot();
// TODO: The item should be dropped to the container our owner is in, if any.
var itemTransform = item.Owner.GetComponent<ITransformComponent>();
itemTransform.LocalPosition = coords ?? Owner.GetComponent<ITransformComponent>().LocalPosition;
item.Owner.Transform.LocalPosition = coords;
Dirty();
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>
/// Checks whether an item can be dropped from the specified slot.
/// </summary>
@@ -212,10 +327,12 @@ namespace Content.Server.GameObjects
{
orderedHands.Add(index);
}
if (ActiveIndex == null)
{
ActiveIndex = index;
}
Dirty();
}
@@ -241,6 +358,7 @@ namespace Content.Server.GameObjects
activeIndex = orderedHands[0];
}
}
Dirty();
}
@@ -264,6 +382,7 @@ namespace Content.Server.GameObjects
dict[hand.Key] = hand.Value.ContainedEntity.Uid;
}
}
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);
switch (message)
{
case ClientChangedHandMsg msg:
{
var playerMan = IoCManager.Resolve<IPlayerManager>();
var session = playerMan.GetSessionByChannel(netChannel);
var playerentity = session.AttachedEntity;
{
var playerMan = IoCManager.Resolve<IPlayerManager>();
var session = playerMan.GetSessionByChannel(netChannel);
var playerEntity = session.AttachedEntity;
if (playerentity == Owner && HasHand(msg.Index))
ActiveIndex = msg.Index;
break;
if (playerEntity == Owner && HasHand(msg.Index))
ActiveIndex = msg.Index;
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:
{
var playerMan = IoCManager.Resolve<IPlayerManager>();
var session = playerMan.GetSessionByChannel(netChannel);
var playerentity = session.AttachedEntity;
var used = GetActiveHand?.Owner;
{
var playerMan = IoCManager.Resolve<IPlayerManager>();
var session = playerMan.GetSessionByChannel(netChannel);
var playerEntity = session.AttachedEntity;
var used = GetActiveHand?.Owner;
if (playerentity == Owner && used != null)
{
InteractionSystem.TryUseInteraction(Owner, used);
}
break;
if (playerEntity == Owner && used != null)
{
InteractionSystem.TryUseInteraction(Owner, used);
}
break;
}
}
}
}

View File

@@ -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.Components.Container;
using SS14.Shared.Enums;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Interactable
{
/// <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>
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 SpriteComponent _spriteComponent;
[ViewVariables] private ContainerSlot _cellContainer;
[ViewVariables]
private PowerCellComponent Cell
{
get
{
if (_cellContainer.ContainedEntity == null)
{
return null;
}
if (_cellContainer.ContainedEntity == null) return null;
_cellContainer.ContainedEntity.TryGetComponent(out PowerCellComponent cell);
return cell;
@@ -35,12 +36,33 @@ namespace Content.Server.GameObjects.Components.Interactable
public override string Name => "HandheldLight";
/// <summary>
/// Status of light, whether or not it is emitting light.
/// Status of light, whether or not it is emitting light.
/// </summary>
[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()
{
@@ -58,13 +80,8 @@ namespace Content.Server.GameObjects.Components.Interactable
}
}
bool IUse.UseEntity(IEntity user)
{
return ToggleStatus();
}
/// <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>
/// <returns>True if the light's status was toggled, false otherwise.</returns>
public bool ToggleStatus()
@@ -90,10 +107,7 @@ namespace Content.Server.GameObjects.Components.Interactable
public void TurnOff()
{
if (!Activated)
{
return;
}
if (!Activated) return;
_spriteComponent.LayerSetState(0, "lantern_off");
_pointLight.State = LightState.Off;
@@ -102,50 +116,57 @@ namespace Content.Server.GameObjects.Components.Interactable
public void TurnOn()
{
if (Activated)
{
return;
}
if (Activated) return;
var cell = Cell;
if (cell == null)
{
return;
}
if (cell == null) return;
// 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.
// Simple enough.
if (cell.AvailableCharge(1) < Wattage)
{
return;
}
if (cell.AvailableCharge(1) < Wattage) return;
_spriteComponent.LayerSetState(0, "lantern_on");
_pointLight.State = LightState.On;
}
string IExamine.Examine()
{
if (Activated)
{
return "The light is currently on.";
}
return null;
}
public void OnUpdate(float frameTime)
{
if (!Activated)
{
return;
}
if (!Activated) return;
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);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace Content.Server.GameObjects.EntitySystems
input.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem));
input.BindMap.BindFunction(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem));
}
/// <inheritdoc />
public override void Shutdown()
{
@@ -118,13 +118,14 @@ namespace Content.Server.GameObjects.EntitySystems
var transform = ent.Transform;
GridLocalCoordinates? dropPos = null;
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)
@@ -160,7 +161,7 @@ namespace Content.Server.GameObjects.EntitySystems
stackComp.Use(1);
throwEnt = throwEnt.EntityManager.ForceSpawnEntityAt(throwEnt.Prototype.ID, plyEnt.Transform.LocalPosition);
}
if (!throwEnt.TryGetComponent(out CollidableComponent colComp))
{
colComp = throwEnt.AddComponent<CollidableComponent>();
@@ -180,7 +181,7 @@ namespace Content.Server.GameObjects.EntitySystems
{
projComp = throwEnt.AddComponent<ThrownItemComponent>();
}
projComp.IgnoreEntity(plyEnt);
var transform = plyEnt.Transform;

View 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;
}
}
}
}
}

View File

@@ -1,6 +1,8 @@
using Content.Server.GameObjects;
using System;
using Content.Server.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using System.Collections.Generic;
using SS14.Server.GameObjects.Components.Container;
using SS14.Shared.Map;
namespace Content.Server.Interfaces.GameObjects
@@ -69,12 +71,96 @@ namespace Content.Server.Interfaces.GameObjects
bool CanPutInHand(ItemComponent item, string index);
/// <summary>
/// Drops an item on the ground, removing it from the hand.
/// Finds the hand slot holding the specified entity, if any.
/// </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>
/// <returns>True if an item was successfully dropped, false otherwise.</returns>
bool Drop(string index, GridLocalCoordinates? coords);
/// <returns>True if an item was dropped, false otherwise.</returns>
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>
/// Checks whether the item in the specified hand can be dropped.