using System;
using Robust.Client.GameObjects.Components;
using Robust.Client.Interfaces.Input;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Players;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects.EntitySystems
{
///
/// Client-side processing of all input commands through the simulation.
///
public class InputSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IPlayerManager _playerManager;
#pragma warning restore 649
private readonly IPlayerCommandStates _cmdStates = new PlayerCommandStates();
private readonly CommandBindMapping _bindMap = new CommandBindMapping();
///
/// Current states for all of the keyFunctions.
///
public IPlayerCommandStates CmdStates => _cmdStates;
///
/// Holds the keyFunction -> handler bindings for the simulation.
///
public ICommandBindMapping BindMap => _bindMap;
///
/// Inserts an Input Command into the simulation.
///
/// Player session that raised the command. On client, this is always the LocalPlayer session.
/// Function that is being changed.
/// Arguments for this event.
public void HandleInputCommand(ICommonSession session, BoundKeyFunction function, FullInputCmdMessage message)
{
#if DEBUG
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(function);
DebugTools.Assert(funcId == message.InputFunctionId, "Function ID in message does not match function.");
#endif
// set state, state change is updated regardless if it is locally bound
_cmdStates.SetState(function, message.State);
// handle local binds before sending off
if (_bindMap.TryGetHandler(function, out var handler))
{
// local handlers can block sending over the network.
if (handler.HandleCmdMessage(session, message))
return;
}
RaiseNetworkEvent(message);
}
///
public override void SubscribeEvents()
{
base.SubscribeEvents();
SubscribeEvent(OnAttachedEntityChanged);
}
private void OnAttachedEntityChanged(object sender, EntitySystemMessage message)
{
if(!(message is PlayerAttachSysMessage msg))
return;
if (msg.AttachedEntity != null) // attach
{
SetEntityContextActive(_inputManager, msg.AttachedEntity);
}
else // detach
{
_inputManager.Contexts.SetActiveContext(InputContextContainer.DefaultContextName);
}
}
private static void SetEntityContextActive(IInputManager inputMan, IEntity entity)
{
if(entity == null || !entity.IsValid())
throw new ArgumentNullException(nameof(entity));
if (!entity.TryGetComponent(out InputComponent inputComp))
{
Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity.Uid}, entProto={entity.Prototype}");
return;
}
if (inputMan.Contexts.Exists(inputComp.ContextName))
{
inputMan.Contexts.SetActiveContext(inputComp.ContextName);
}
else
{
Logger.ErrorS("input.context", $"Unknown context: entId={entity.Uid}, entProto={entity.Prototype}, context={inputComp.ContextName}");
}
}
///
/// Sets the active context to the defined context on the attached entity.
///
public void SetEntityContextActive()
{
if (_playerManager.LocalPlayer.ControlledEntity == null)
{
return;
}
SetEntityContextActive(_inputManager, _playerManager.LocalPlayer.ControlledEntity);
}
}
///
/// Entity system message that is raised when the player changes attached entities.
///
public class PlayerAttachSysMessage : EntitySystemMessage
{
///
/// New entity the player is attached to.
///
public IEntity AttachedEntity { get; }
///
/// Creates a new instance of .
///
/// New entity the player is attached to.
public PlayerAttachSysMessage(IEntity attachedEntity)
{
AttachedEntity = attachedEntity;
}
}
}