Combat mode improvements.

You now need to hold the mouse for 0.15 seconds in combat mode to actually do an attack.

Otherwise it's regular item interaction (for now).
This commit is contained in:
Pieter-Jan Briers
2020-01-26 03:38:51 +01:00
parent 7e43d574d8
commit 2ec493e2af
6 changed files with 182 additions and 15 deletions

View File

@@ -1,23 +1,62 @@
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.UserInterface; using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Player;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using static Content.Client.StaticIoC;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.GameObjects.EntitySystems
{ {
public sealed class CombatModeSystem : EntitySystem public sealed class CombatModeSystem : EntitySystem
{ {
private const float AttackTimeThreshold = 0.15f;
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud; [Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IOverlayManager _overlayManager;
#pragma warning restore 649 #pragma warning restore 649
private InputSystem _inputSystem;
public bool UseOrAttackIsDown { get; private set; }
private float _timeHeld;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_gameHud.OnCombatModeChanged = OnCombatModeChanged; _gameHud.OnCombatModeChanged = OnCombatModeChanged;
_gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged; _gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged;
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
_inputSystem.BindMap.BindFunction(ContentKeyFunctions.UseOrAttack, new InputHandler(this));
_overlayManager.AddOverlay(new CombatModeOverlay(this));
}
private bool IsInCombatMode()
{
var entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out CombatModeComponent combatMode))
{
return false;
}
return combatMode.IsInCombatMode;
} }
private void OnTargetingZoneChanged(TargetingZone obj) private void OnTargetingZoneChanged(TargetingZone obj)
@@ -28,6 +67,104 @@ namespace Content.Client.GameObjects.EntitySystems
private void OnCombatModeChanged(bool obj) private void OnCombatModeChanged(bool obj)
{ {
RaiseNetworkEvent(new CombatModeSystemMessages.SetCombatModeActiveMessage(obj)); RaiseNetworkEvent(new CombatModeSystemMessages.SetCombatModeActiveMessage(obj));
// Just in case.
UseOrAttackIsDown = false;
}
private bool HandleInputMessage(ICommonSession session, InputCmdMessage message)
{
if (!(message is FullInputCmdMessage msg))
return false;
void SendMsg(BoundKeyFunction function, BoundKeyState state)
{
var functionId = _inputManager.NetworkBindMap.KeyFunctionID(function);
var sendMsg = new FullInputCmdMessage(msg.Tick, functionId, state,
msg.Coordinates, msg.ScreenCoordinates, msg.Uid);
_inputSystem.HandleInputCommand(session, function, sendMsg);
}
// If we are not in combat mode, relay it as a regular Use instead.
if (!IsInCombatMode())
{
SendMsg(EngineKeyFunctions.Use, msg.State);
return true;
}
if (msg.State == BoundKeyState.Down)
{
UseOrAttackIsDown = true;
_timeHeld = 0;
return true;
}
// Up.
if (_timeHeld >= AttackTimeThreshold)
{
// Attack.
SendMsg(ContentKeyFunctions.Attack, BoundKeyState.Down);
SendMsg(ContentKeyFunctions.Attack, BoundKeyState.Up);
}
else
{
// Use.
SendMsg(EngineKeyFunctions.Use, BoundKeyState.Down);
SendMsg(EngineKeyFunctions.Use, BoundKeyState.Up);
}
UseOrAttackIsDown = false;
return true;
}
public override void FrameUpdate(float frameTime)
{
if (UseOrAttackIsDown)
{
_timeHeld += frameTime;
}
}
// Custom input handler type so we get the ENTIRE InputCmdMessage.
private sealed class InputHandler : InputCmdHandler
{
private readonly CombatModeSystem _combatModeSystem;
public InputHandler(CombatModeSystem combatModeSystem)
{
_combatModeSystem = combatModeSystem;
}
public override bool HandleCmdMessage(ICommonSession session, InputCmdMessage message)
{
return _combatModeSystem.HandleInputMessage(session, message);
}
}
private sealed class CombatModeOverlay : Overlay
{
private readonly CombatModeSystem _system;
public CombatModeOverlay(CombatModeSystem system) : base(nameof(CombatModeOverlay))
{
_system = system;
}
protected override void Draw(DrawingHandleBase handle)
{
var screenHandle = (DrawingHandleScreen) handle;
var mousePos = IoCManager.Resolve<IInputManager>().MouseScreenPosition;
if (_system.UseOrAttackIsDown && _system._timeHeld > AttackTimeThreshold)
{
var tex = ResC.GetTexture($"/Textures/Objects/Tools/toolbox_r.png");
screenHandle.DrawTextureRect(tex, UIBox2.FromDimensions(mousePos, tex.Size * 2));
}
}
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Client.GameObjects.Components.Weapons.Ranged; using Content.Client.GameObjects.Components.Weapons.Ranged;
using Content.Client.Interfaces.GameObjects; using Content.Client.Interfaces.GameObjects;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems; using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input; using Robust.Client.Interfaces.Input;
@@ -20,6 +21,7 @@ namespace Content.Client.GameObjects.EntitySystems
#pragma warning restore 649 #pragma warning restore 649
private InputSystem _inputSystem; private InputSystem _inputSystem;
private CombatModeSystem _combatModeSystem;
private bool _isFirstShot; private bool _isFirstShot;
private bool _blocked; private bool _blocked;
@@ -29,6 +31,7 @@ namespace Content.Client.GameObjects.EntitySystems
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>(); _inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
_combatModeSystem = EntitySystemManager.GetEntitySystem<CombatModeSystem>();
} }
public override void Update(float frameTime) public override void Update(float frameTime)
@@ -36,8 +39,8 @@ namespace Content.Client.GameObjects.EntitySystems
base.Update(frameTime); base.Update(frameTime);
var canFireSemi = _isFirstShot; var canFireSemi = _isFirstShot;
var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use); var state = _inputSystem.CmdStates.GetState(ContentKeyFunctions.Attack);
if (state != BoundKeyState.Down) if (!_combatModeSystem.UseOrAttackIsDown && state != BoundKeyState.Down)
{ {
_isFirstShot = true; _isFirstShot = true;
_blocked = false; _blocked = false;

View File

@@ -15,6 +15,7 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.FocusChat); common.AddFunction(ContentKeyFunctions.FocusChat);
common.AddFunction(ContentKeyFunctions.ExamineEntity); common.AddFunction(ContentKeyFunctions.ExamineEntity);
common.AddFunction(ContentKeyFunctions.OpenTutorial); common.AddFunction(ContentKeyFunctions.OpenTutorial);
common.AddFunction(ContentKeyFunctions.UseOrAttack);
var human = contexts.GetContext("human"); var human = contexts.GetContext("human");
human.AddFunction(ContentKeyFunctions.SwapHands); human.AddFunction(ContentKeyFunctions.SwapHands);
@@ -28,6 +29,7 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu); human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
human.AddFunction(ContentKeyFunctions.MouseMiddle); human.AddFunction(ContentKeyFunctions.MouseMiddle);
human.AddFunction(ContentKeyFunctions.ToggleCombatMode); human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
human.AddFunction(ContentKeyFunctions.Attack);
var ghost = contexts.New("ghost", "common"); var ghost = contexts.New("ghost", "common");
ghost.AddFunction(EngineKeyFunctions.MoveUp); ghost.AddFunction(EngineKeyFunctions.MoveUp);

View File

@@ -257,6 +257,8 @@ namespace Content.Server.GameObjects.EntitySystems
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>(); var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(EngineKeyFunctions.Use, inputSys.BindMap.BindFunction(EngineKeyFunctions.Use,
new PointerInputCmdHandler(HandleUseItemInHand)); new PointerInputCmdHandler(HandleUseItemInHand));
inputSys.BindMap.BindFunction(ContentKeyFunctions.Attack,
new PointerInputCmdHandler(HandleAttack));
inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld, inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld,
new PointerInputCmdHandler(HandleActivateItemInWorld)); new PointerInputCmdHandler(HandleActivateItemInWorld));
} }
@@ -313,6 +315,37 @@ namespace Content.Server.GameObjects.EntitySystems
activateComp.Activate(new ActivateEventArgs {User = user}); activateComp.Activate(new ActivateEventArgs {User = user});
} }
private bool HandleAttack(ICommonSession session, GridCoordinates coords, EntityUid uid)
{
// client sanitization
if (!_mapManager.GridExists(coords.GridID))
{
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return true;
}
if (uid.IsClientSide())
{
Logger.WarningS("system.interaction",
$"Client sent attack with client-side entity. Session={session}, Uid={uid}");
return true;
}
var userEntity = ((IPlayerSession) session).AttachedEntity;
if (userEntity == null || !userEntity.IsValid())
{
return true;
}
if (userEntity.TryGetComponent(out CombatModeComponent combatMode) && combatMode.IsInCombatMode)
{
DoAttack(userEntity, coords);
}
return true;
}
private bool HandleUseItemInHand(ICommonSession session, GridCoordinates coords, EntityUid uid) private bool HandleUseItemInHand(ICommonSession session, GridCoordinates coords, EntityUid uid)
{ {
// client sanitization // client sanitization
@@ -336,14 +369,7 @@ namespace Content.Server.GameObjects.EntitySystems
return true; return true;
} }
if (userEntity.TryGetComponent(out CombatModeComponent combatMode) && combatMode.IsInCombatMode)
{
DoAttack(userEntity, coords);
}
else
{
UserInteraction(userEntity, coords, uid); UserInteraction(userEntity, coords, uid);
}
return true; return true;
} }
@@ -561,7 +587,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// </summary> /// </summary>
public void UseInteraction(IEntity user, IEntity used) public void UseInteraction(IEntity user, IEntity used)
{ {
if (used.TryGetComponent<UseDelayComponent>(out var delayComponent)) if (used.TryGetComponent<UseDelayComponent>(out var delayComponent))
{ {
if (delayComponent.ActiveDelay) if (delayComponent.ActiveDelay)
@@ -600,7 +625,6 @@ namespace Content.Server.GameObjects.EntitySystems
ThrownInteraction(user, item); ThrownInteraction(user, item);
return true; return true;
} }
/// <summary> /// <summary>
@@ -657,7 +681,6 @@ namespace Content.Server.GameObjects.EntitySystems
DroppedInteraction(user, item); DroppedInteraction(user, item);
return true; return true;
} }
/// <summary> /// <summary>

View File

@@ -5,6 +5,8 @@ namespace Content.Shared.Input
[KeyFunctions] [KeyFunctions]
public static class ContentKeyFunctions public static class ContentKeyFunctions
{ {
public static readonly BoundKeyFunction UseOrAttack = "UseOrAttack";
public static readonly BoundKeyFunction Attack = "Attack";
public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand"; public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand";
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 Drop = "Drop"; public static readonly BoundKeyFunction Drop = "Drop";

View File

@@ -1,6 +1,6 @@
version: 1 # Not used right now, whatever. version: 1 # Not used right now, whatever.
binds: binds:
- function: Use - function: UseOrAttack
type: state type: state
key: MouseLeft key: MouseLeft
canFocus: true canFocus: true