diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj
index 71024b6d7d..d7632f5255 100644
--- a/Content.Client/Content.Client.csproj
+++ b/Content.Client/Content.Client.csproj
@@ -127,6 +127,7 @@
+
diff --git a/Content.Client/EscapeMenuOwner.cs b/Content.Client/EscapeMenuOwner.cs
index 667026cd53..d9e41b1c20 100644
--- a/Content.Client/EscapeMenuOwner.cs
+++ b/Content.Client/EscapeMenuOwner.cs
@@ -1,12 +1,10 @@
using Content.Client.UserInterface;
using Robust.Client.Console;
-using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.State.States;
-using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map;
@@ -19,7 +17,6 @@ namespace Content.Client
{
#pragma warning disable 649
[Dependency] private readonly IStateManager _stateManager;
- [Dependency] private readonly IDisplayManager _displayManager;
[Dependency] private readonly IClientConsole _clientConsole;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
[Dependency] private readonly IPlacementManager _placementManager;
@@ -41,7 +38,7 @@ namespace Content.Client
if (obj.NewState is GameScreen)
{
// Switched TO GameScreen.
- _escapeMenu = new EscapeMenu(_displayManager, _clientConsole, _tileDefinitionManager, _placementManager,
+ _escapeMenu = new EscapeMenu(_clientConsole, _tileDefinitionManager, _placementManager,
_prototypeManager, _resourceCache, _configurationManager)
{
Visible = false
diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs
index e92e565fcc..1ae9c5d814 100644
--- a/Content.Client/GameTicking/ClientGameTicker.cs
+++ b/Content.Client/GameTicking/ClientGameTicker.cs
@@ -44,6 +44,7 @@ namespace Content.Client.GameTicking
[ViewVariables] private LobbyGui _lobby;
[ViewVariables] private bool _gameStarted;
[ViewVariables] private DateTime _startTime;
+ [ViewVariables] private TutorialButton _tutorialButton;
public void Initialize()
{
@@ -66,6 +67,11 @@ namespace Content.Client.GameTicking
return;
}
+ _updatePlayerList();
+ }
+
+ private void _updatePlayerList()
+ {
_lobby.OnlinePlayerItemList.Clear();
foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name))
{
@@ -165,6 +171,12 @@ namespace Content.Client.GameTicking
_gameChat = null;
}
+ if (_tutorialButton != null)
+ {
+ _tutorialButton.Dispose();
+ _tutorialButton = null;
+ }
+
_tickerState = TickerState.InLobby;
_lobby = new LobbyGui(_localization, _resourceCache);
@@ -208,6 +220,8 @@ namespace Content.Client.GameTicking
};
_lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect");
+
+ _updatePlayerList();
}
private void _joinGame(MsgTickerJoinGame message)
@@ -235,6 +249,9 @@ namespace Content.Client.GameTicking
_gameChat = new ChatBox();
_userInterfaceManager.StateRoot.AddChild(_gameChat);
_chatManager.SetChatBox(_gameChat);
+ _tutorialButton = new TutorialButton();
+ _userInterfaceManager.StateRoot.AddChild(_tutorialButton);
+ _tutorialButton.SetAnchorAndMarginPreset(Control.LayoutPreset.BottomLeft, Control.LayoutPresetMode.MinSize, 50);
_gameChat.DefaultChatFormat = "say \"{0}\"";
}
diff --git a/Content.Client/UserInterface/EscapeMenu.cs b/Content.Client/UserInterface/EscapeMenu.cs
index 324d9590da..a13bae00e5 100644
--- a/Content.Client/UserInterface/EscapeMenu.cs
+++ b/Content.Client/UserInterface/EscapeMenu.cs
@@ -18,7 +18,6 @@ namespace Content.Client.UserInterface
private readonly IPlacementManager _placementManager;
private readonly IPrototypeManager _prototypeManager;
private readonly IResourceCache _resourceCache;
- private readonly IDisplayManager _displayManager;
private readonly IConfigurationManager _configSystem;
private BaseButton QuitButton;
@@ -27,16 +26,14 @@ namespace Content.Client.UserInterface
private BaseButton SpawnTilesButton;
private OptionsMenu optionsMenu;
- public EscapeMenu(IDisplayManager displayManager,
- IClientConsole console,
+ public EscapeMenu(IClientConsole console,
ITileDefinitionManager tileDefinitionManager,
IPlacementManager placementManager,
IPrototypeManager prototypeManager,
IResourceCache resourceCache,
- IConfigurationManager configSystem) : base(displayManager)
+ IConfigurationManager configSystem)
{
_configSystem = configSystem;
- _displayManager = displayManager;
_console = console;
__tileDefinitionManager = tileDefinitionManager;
_placementManager = placementManager;
@@ -48,7 +45,7 @@ namespace Content.Client.UserInterface
private void PerformLayout()
{
- optionsMenu = new OptionsMenu(_displayManager, _configSystem)
+ optionsMenu = new OptionsMenu(_configSystem)
{
Visible = false
};
@@ -97,14 +94,14 @@ namespace Content.Client.UserInterface
private void OnSpawnEntitiesButtonClicked(BaseButton.ButtonEventArgs args)
{
- var window = new EntitySpawnWindow(_displayManager, _placementManager, _prototypeManager, _resourceCache);
+ var window = new EntitySpawnWindow(_placementManager, _prototypeManager, _resourceCache);
window.AddToScreen();
window.OpenToLeft();
}
private void OnSpawnTilesButtonClicked(BaseButton.ButtonEventArgs args)
{
- var window = new TileSpawnWindow(__tileDefinitionManager, _placementManager, _displayManager, _resourceCache);
+ var window = new TileSpawnWindow(__tileDefinitionManager, _placementManager, _resourceCache);
window.AddToScreen();
window.OpenToLeft();
}
diff --git a/Content.Client/UserInterface/TutorialButton.cs b/Content.Client/UserInterface/TutorialButton.cs
new file mode 100644
index 0000000000..1e72981a6c
--- /dev/null
+++ b/Content.Client/UserInterface/TutorialButton.cs
@@ -0,0 +1,61 @@
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared.Utility;
+
+namespace Content.Client.UserInterface
+{
+ internal sealed class TutorialButton : Button
+ {
+ private const string TutorialContents = @"Hi and welcome to Space Station 14!
+
+This tutorial will assume that you know a bit about how SS13 plays.
+It's mostly intended to lay out the controls and their differences from SS13.
+
+Just like in any game, WASD is movement. If that does not work, the server probably broke.
+
+Clicking on things ""interacts"" in some object-defined sense with it, with your active hand.
+
+X switches hands. Z uses the item in your hand. Q drops items. T focuses chat. C opens your inventory.
+
+New to SS14: You can press ""E"" to activate objects. This functions similarly to clicking with an empty hand most of the time: opens interfaces, etc. The difference is that it works even without an empty hand. No longer do you need to drop your tools to use a computer!
+
+You can talk in OOC by prefixing the message with [ or /ooc.
+
+If you are not on a QWERTY keyboard, the keys mentioned above are bound to the physical location on your keyboard,
+not what letter they correspond to. For example on AZERTY movement is ZQSD, drop is A, W is activate in hand.
+
+If you have any feedback, questions, bug reports, etc..., do not be afraid to tell us!
+You can ask on Discord or heck, just write it in OOC, we'll catch it.
+";
+
+
+ public TutorialButton()
+ {
+ OnPressed += OnOnPressed;
+
+ Text = "Tutorial";
+ }
+
+ private void OnOnPressed(ButtonEventArgs obj)
+ {
+ _openTutorialWindow();
+ }
+
+ private void _openTutorialWindow()
+ {
+ var window = new SS14Window {Title = "Tutorial"};
+
+ var scrollContainer = new ScrollContainer();
+ window.Contents.AddChild(scrollContainer);
+
+ var label = new RichTextLabel();
+ scrollContainer.AddChild(label);
+
+ var message = new FormattedMessage();
+ message.AddText(TutorialContents);
+ label.SetMessage(message);
+
+ window.AddToScreen();
+ }
+ }
+}
diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj
index 6eb2763531..8495a115da 100644
--- a/Content.Server/Content.Server.csproj
+++ b/Content.Server/Content.Server.csproj
@@ -142,7 +142,7 @@
-
+
diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs
index 98f41d7d90..450519faa9 100644
--- a/Content.Server/EntryPoint.cs
+++ b/Content.Server/EntryPoint.cs
@@ -88,6 +88,7 @@ namespace Content.Server
factory.Register();
factory.Register();
factory.Register();
+ factory.RegisterReference();
//Power Components
factory.Register();
@@ -127,6 +128,7 @@ namespace Content.Server
factory.RegisterReference();
factory.Register();
factory.RegisterReference();
+ factory.RegisterReference();
factory.Register();
factory.Register();
@@ -135,6 +137,7 @@ namespace Content.Server
factory.Register();
factory.Register();
factory.Register();
+ factory.RegisterReference();
factory.Register();
factory.Register();
factory.Register();
@@ -152,6 +155,7 @@ namespace Content.Server
factory.RegisterReference();
factory.Register();
+ factory.RegisterReference();
factory.Register();
factory.RegisterReference();
diff --git a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
index 18d8072b38..c6e8d3f656 100644
--- a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
+++ b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
@@ -93,6 +93,10 @@ namespace Content.Server.GameObjects.Components.Construction
bool TryProcessStep(ConstructionStep step, IEntity slapped)
{
+ if (step == null)
+ {
+ return false;
+ }
var sound = IoCManager.Resolve().GetEntitySystem();
switch (step)
diff --git a/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs
index 2a27c6ccdb..92562e246b 100644
--- a/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs
+++ b/Content.Server/GameObjects/Components/Construction/ConstructorComponent.cs
@@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Construction
var prototype = _prototypeManager.Index(prototypeName);
var transform = Owner.Transform;
- if (!loc.InRange(_mapManager, transform.GridPosition, InteractionSystem.INTERACTION_RANGE))
+ if (!loc.InRange(_mapManager, transform.GridPosition, InteractionSystem.InteractionRange))
{
return;
}
diff --git a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs
index 3100198fd6..6f9957d617 100644
--- a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs
+++ b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs
@@ -10,7 +10,7 @@ using Robust.Shared.Timers;
namespace Content.Server.GameObjects
{
- public class ServerDoorComponent : Component, IAttackHand
+ public class ServerDoorComponent : Component, IActivate
{
public override string Name => "Door";
@@ -41,7 +41,7 @@ namespace Content.Server.GameObjects
base.OnRemove();
}
- public bool AttackHand(AttackHandEventArgs eventArgs)
+ void IActivate.Activate(ActivateEventArgs eventArgs)
{
if (_state == DoorState.Open)
{
@@ -51,7 +51,6 @@ namespace Content.Server.GameObjects
{
Open();
}
- return true;
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
diff --git a/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs b/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs
index 3822f1d39a..d24586a3d0 100644
--- a/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs
+++ b/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs
@@ -462,8 +462,8 @@ namespace Content.Server.GameObjects
var playerEntity = session.AttachedEntity;
var used = GetActiveHand?.Owner;
- if (playerEntity == Owner && used != null)
- {
+ if (playerEntity == Owner && used != null && slot.ContainedEntity != null)
+ {
var interactionSystem = _entitySystemManager.GetEntitySystem();
interactionSystem.Interaction(Owner, used, slot.ContainedEntity,
GridCoordinates.Nullspace);
diff --git a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs
index 783ecdd774..327c8a089d 100644
--- a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs
+++ b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs
@@ -14,7 +14,7 @@ using Robust.Server.GameObjects;
namespace Content.Server.GameObjects.Components
{
- public class EntityStorageComponent : Component, IAttackHand, IStorageComponent
+ public class EntityStorageComponent : Component, IActivate, IStorageComponent
{
public override string Name => "EntityStorage";
@@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components
[ViewVariables]
public bool Open { get; private set; }
- public bool AttackHand(AttackHandEventArgs eventArgs)
+ void IActivate.Activate(ActivateEventArgs eventArgs)
{
if (Open)
{
@@ -51,7 +51,6 @@ namespace Content.Server.GameObjects.Components
{
OpenStorage();
}
- return true;
}
private void CloseStorage()
diff --git a/Content.Server/GameObjects/Components/Power/ApcComponent.cs b/Content.Server/GameObjects/Components/Power/ApcComponent.cs
index 51a5130131..62206488fb 100644
--- a/Content.Server/GameObjects/Components/Power/ApcComponent.cs
+++ b/Content.Server/GameObjects/Components/Power/ApcComponent.cs
@@ -12,7 +12,7 @@ using Robust.Shared.IoC;
namespace Content.Server.GameObjects.Components.Power
{
- public sealed class ApcComponent : SharedApcComponent, IAttackHand
+ public sealed class ApcComponent : SharedApcComponent, IActivate
{
PowerStorageComponent Storage;
AppearanceComponent Appearance;
@@ -106,15 +106,14 @@ namespace Content.Server.GameObjects.Components.Power
return net.Lack > 0 ? ApcExternalPowerState.Low : ApcExternalPowerState.Good;
}
- bool IAttackHand.AttackHand(AttackHandEventArgs eventArgs)
+ void IActivate.Activate(ActivateEventArgs eventArgs)
{
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
{
- return false;
+ return;
}
_userInterface.Open(actor.playerSession);
- return true;
}
private void _clickSound()
diff --git a/Content.Server/GameObjects/Components/Research/LatheComponent.cs b/Content.Server/GameObjects/Components/Research/LatheComponent.cs
index 5e55075e53..dff3661d66 100644
--- a/Content.Server/GameObjects/Components/Research/LatheComponent.cs
+++ b/Content.Server/GameObjects/Components/Research/LatheComponent.cs
@@ -15,7 +15,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Research
{
- public class LatheComponent : SharedLatheComponent, IAttackHand, IAttackBy, IActivate
+ public class LatheComponent : SharedLatheComponent, IAttackBy, IActivate
{
public const int VolumePerSheet = 3750;
@@ -94,17 +94,6 @@ namespace Content.Server.GameObjects.Components.Research
_userInterface.Open(actor.playerSession);
return;
}
-
- bool IAttackHand.AttackHand(AttackHandEventArgs eventArgs)
- {
-
- if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
- return false;
-
- _userInterface.Open(actor.playerSession);
- return true;
- }
-
bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out MaterialStorageComponent storage)
diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs
index 9368ee223d..602b359cb6 100644
--- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs
@@ -1,19 +1,19 @@
using System;
-using Content.Server.Interfaces.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.GameObjects.Systems;
-using Robust.Shared.Interfaces.GameObjects;
-using System.Collections.Generic;
using System.Linq;
+using Content.Server.Interfaces.GameObjects;
using Content.Shared.Input;
-using Robust.Shared.Input;
-using Robust.Shared.Log;
-using Robust.Shared.Map;
+using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Systems;
+using Robust.Shared.Input;
+using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
+using Robust.Shared.Log;
+using Robust.Shared.Map;
using Robust.Shared.Players;
namespace Content.Server.GameObjects.EntitySystems
@@ -26,9 +26,6 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Called when using one object on another
///
- ///
- ///
- ///
bool AttackBy(AttackByEventArgs eventArgs);
}
@@ -47,8 +44,6 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Called when a player directly interacts with an empty hand
///
- ///
- ///
bool AttackHand(AttackHandEventArgs eventArgs);
}
@@ -65,13 +60,11 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Called when we try to interact with an entity out of range
///
- ///
- ///
- ///
///
bool RangedAttackBy(RangedAttackByEventArgs eventArgs);
}
+ [PublicAPI]
public class RangedAttackByEventArgs : EventArgs
{
public IEntity User { get; set; }
@@ -88,9 +81,6 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior
///
- ///
- ///
- /// The entity that was clicked on out of range. May be null if no entity was clicked on.true
void AfterAttack(AfterAttackEventArgs eventArgs);
}
@@ -109,7 +99,6 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Called when we activate an object we are holding to use it
///
- ///
///
bool UseEntity(UseEntityEventArgs eventArgs);
}
@@ -127,7 +116,6 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Called when this component is activated by another entity.
///
- /// Entity that activated this component.
void Activate(ActivateEventArgs eventArgs);
}
@@ -139,50 +127,66 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Governs interactions during clicking on entities
///
- public class InteractionSystem : EntitySystem
+ [UsedImplicitly]
+ public sealed class InteractionSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649
- public const float INTERACTION_RANGE = 2;
- public const float INTERACTION_RANGE_SQUARED = INTERACTION_RANGE * INTERACTION_RANGE;
+ public const float InteractionRange = 2;
+ public const float InteractionRangeSquared = InteractionRange * InteractionRange;
public override void Initialize()
{
var inputSys = EntitySystemManager.GetEntitySystem();
- inputSys.BindMap.BindFunction(ContentKeyFunctions.UseItemInHand, new PointerInputCmdHandler(HandleUseItemInHand));
- inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld, new PointerInputCmdHandler((HandleUseItemInWorld)));
+ inputSys.BindMap.BindFunction(ContentKeyFunctions.UseItemInHand,
+ new PointerInputCmdHandler(HandleUseItemInHand));
+ inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld,
+ new PointerInputCmdHandler((HandleActivateItemInWorld)));
}
- private void HandleUseItemInWorld(ICommonSession session, GridCoordinates coords, EntityUid uid)
+ public void HandleActivateItemInWorld(ICommonSession session, GridCoordinates coords, EntityUid uid)
{
- if(!EntityManager.TryGetEntity(uid, out var used))
+ if (!EntityManager.TryGetEntity(uid, out var used))
return;
var playerEnt = ((IPlayerSession) session).AttachedEntity;
- if(playerEnt == null || !playerEnt.IsValid())
+ if (playerEnt == null || !playerEnt.IsValid())
+ {
return;
+ }
- if (!playerEnt.Transform.GridPosition.InRange(_mapManager, used.Transform.GridPosition, INTERACTION_RANGE))
+ if (!playerEnt.Transform.GridPosition.InRange(_mapManager, used.Transform.GridPosition, InteractionRange))
+ {
return;
+ }
- var activateMsg = new ActivateInWorldMessage(playerEnt, used);
+ InteractionActivate(playerEnt, used);
+ }
+
+ private void InteractionActivate(IEntity user, IEntity used)
+ {
+ var activateMsg = new ActivateInWorldMessage(user, used);
RaiseEvent(activateMsg);
- if(activateMsg.Handled)
+ if (activateMsg.Handled)
+ {
return;
+ }
if (!used.TryGetComponent(out IActivate activateComp))
+ {
return;
+ }
- activateComp.Activate(new ActivateEventArgs { User = playerEnt });
+ activateComp.Activate(new ActivateEventArgs {User = user});
}
private void HandleUseItemInHand(ICommonSession session, GridCoordinates coords, EntityUid uid)
{
// client sanitization
- if(!_mapManager.GridExists(coords.GridID))
+ if (!_mapManager.GridExists(coords.GridID))
{
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return;
@@ -190,32 +194,37 @@ namespace Content.Server.GameObjects.EntitySystems
if (uid.IsClientSide())
{
- Logger.WarningS("system.interaction", $"Client sent interaction with client-side entity. Session={session}, Uid={uid}");
+ Logger.WarningS("system.interaction",
+ $"Client sent interaction with client-side entity. Session={session}, Uid={uid}");
return;
}
- UserInteraction(((IPlayerSession)session).AttachedEntity, coords, uid);
+ UserInteraction(((IPlayerSession) session).AttachedEntity, coords, uid);
}
private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid)
{
- //Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
+ // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
if (!EntityManager.TryGetEntity(clickedUid, out var attacked))
+ {
attacked = null;
+ }
- //Verify player has a transform component
+ // Verify player has a transform component
if (!player.TryGetComponent(out var playerTransform))
{
return;
}
- //Verify player is on the same map as the entity he clicked on
- else if (_mapManager.GetGrid(coordinates.GridID).ParentMap.Index != playerTransform.MapID)
+
+ // Verify player is on the same map as the entity he clicked on
+ if (_mapManager.GetGrid(coordinates.GridID).ParentMap.Index != playerTransform.MapID)
{
- Logger.Warning(string.Format("Player named {0} clicked on a map he isn't located on", player.Name));
+ Logger.WarningS("system.interaction",
+ $"Player named {player.Name} clicked on a map he isn't located on");
return;
}
- //Verify player has a hand, and find what object he is currently holding in his active hand
+ // Verify player has a hand, and find what object he is currently holding in his active hand
if (!player.TryGetComponent(out var hands))
{
return;
@@ -224,59 +233,64 @@ namespace Content.Server.GameObjects.EntitySystems
var item = hands.GetActiveHand?.Owner;
if (!ActionBlockerSystem.CanInteract(player))
- return;
- //TODO: Check if client should be able to see that object to click on it in the first place, prevent using locaters by firing a laser or something
-
-
- //Clicked on empty space behavior, try using ranged attack
- if (attacked == null && item != null)
- {
- //AFTERATTACK: Check if we clicked on an empty location, if so the only interaction we can do is afterattack
- InteractAfterattack(player, item, coordinates);
- return;
- }
- else if (attacked == null)
{
return;
}
- //Verify attacked object is on the map if we managed to click on it somehow
- if (!attacked.GetComponent().IsMapTransform)
- {
- Logger.Warning(string.Format("Player named {0} clicked on object {1} that isn't currently on the map somehow", player.Name, attacked.Name));
- return;
- }
+ // TODO: Check if client should be able to see that object to click on it in the first place
- //Check if ClickLocation is in object bounds here, if not lets log as warning and see why
- if (attacked.TryGetComponent(out BoundingBoxComponent boundingbox))
+ // Clicked on empty space behavior, try using ranged attack
+ if (attacked == null)
{
- if (!boundingbox.WorldAABB.Contains(coordinates.Position))
+ if (item != null)
{
- Logger.Warning(string.Format("Player {0} clicked {1} outside of its bounding box component somehow", player.Name, attacked.Name));
+ // After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterAttack
+ InteractAfterAttack(player, item, coordinates);
+ }
+
+ return;
+ }
+
+ // Verify attacked object is on the map if we managed to click on it somehow
+ if (!attacked.Transform.IsMapTransform)
+ {
+ Logger.WarningS("system.interaction",
+ $"Player named {player.Name} clicked on object {attacked.Name} that isn't currently on the map somehow");
+ return;
+ }
+
+ // Check if ClickLocation is in object bounds here, if not lets log as warning and see why
+ if (attacked.TryGetComponent(out BoundingBoxComponent boundingBox))
+ {
+ if (!boundingBox.WorldAABB.Contains(coordinates.Position))
+ {
+ Logger.WarningS("system.interaction",
+ $"Player {player.Name} clicked {attacked.Name} outside of its bounding box component somehow");
return;
}
}
- //RANGEDATTACK/AFTERATTACK: Check distance between user and clicked item, if too large parse it in the ranged function
- //TODO: have range based upon the item being used? or base it upon some variables of the player himself?
- var distance = (playerTransform.WorldPosition - attacked.GetComponent().WorldPosition).LengthSquared;
- if (distance > INTERACTION_RANGE_SQUARED)
+ // RangedAttack/AfterAttack: Check distance between user and clicked item, if too large parse it in the ranged function
+ // TODO: have range based upon the item being used? or base it upon some variables of the player himself?
+ var distance = (playerTransform.WorldPosition - attacked.Transform.WorldPosition).LengthSquared;
+ if (distance > InteractionRangeSquared)
{
if (item != null)
{
RangedInteraction(player, item, attacked, coordinates);
return;
}
- return; //Add some form of ranged attackhand here if you need it someday, or perhaps just ways to modify the range of attackhand
+
+ return; // Add some form of ranged AttackHand here if you need it someday, or perhaps just ways to modify the range of AttackHand
}
- //We are close to the nearby object and the object isn't contained in our active hand
- //ATTACKBY/AFTERATTACK: We will either use the item on the nearby object
+ // We are close to the nearby object and the object isn't contained in our active hand
+ // AttackBy/AfterAttack: We will either use the item on the nearby object
if (item != null)
{
Interaction(player, item, attacked, coordinates);
}
- //ATTACKHAND: Since our hand is empty we will use attackhand
+ // AttackHand/Activate: Since our hand is empty we will use AttackHand/Activate
else
{
Interaction(player, attacked);
@@ -284,90 +298,101 @@ namespace Content.Server.GameObjects.EntitySystems
}
///
- /// We didn't click on any entity, try doing an afterattack on the click location
+ /// We didn't click on any entity, try doing an AfterAttack on the click location
///
- ///
- ///
- ///
- public void InteractAfterattack(IEntity user, IEntity weapon, GridCoordinates clicklocation)
+ private void InteractAfterAttack(IEntity user, IEntity weapon, GridCoordinates clickLocation)
{
- var message = new AfterAttackMessage(user, weapon, null, clicklocation);
+ var message = new AfterAttackMessage(user, weapon, null, clickLocation);
RaiseEvent(message);
- if(message.Handled)
- return;
-
- List afterattacks = weapon.GetAllComponents().ToList();
-
- for (var i = 0; i < afterattacks.Count; i++)
+ if (message.Handled)
{
- afterattacks[i].AfterAttack(new AfterAttackEventArgs { User = user, ClickLocation = clicklocation });
+ return;
+ }
+
+ var afterAttacks = weapon.GetAllComponents().ToList();
+ var afterAttackEventArgs = new AfterAttackEventArgs {User = user, ClickLocation = clickLocation};
+
+ foreach (var afterAttack in afterAttacks)
+ {
+ afterAttack.AfterAttack(afterAttackEventArgs);
}
}
///
/// Uses a weapon/object on an entity
- /// Finds interactable components with the Attackby interface and calls their function
+ /// Finds components with the AttackBy interface and calls their function
///
- ///
- ///
- ///
- public void Interaction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clicklocation)
+ public void Interaction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation)
{
- var attackMsg = new AttackByMessage(user, weapon, attacked, clicklocation);
+ var attackMsg = new AttackByMessage(user, weapon, attacked, clickLocation);
RaiseEvent(attackMsg);
- if(attackMsg.Handled)
- return;
-
- List interactables = attacked.GetAllComponents().ToList();
-
- for (var i = 0; i < interactables.Count; i++)
+ if (attackMsg.Handled)
{
- if (interactables[i].AttackBy(new AttackByEventArgs { User = user, ClickLocation = clicklocation, AttackWith = weapon })) //If an attackby returns a status completion we finish our attack
+ return;
+ }
+
+ var attackBys = attacked.GetAllComponents().ToList();
+ var attackByEventArgs = new AttackByEventArgs
+ {
+ User = user, ClickLocation = clickLocation, AttackWith = weapon
+ };
+
+ foreach (var attackBy in attackBys)
+ {
+ if (attackBy.AttackBy(attackByEventArgs))
{
+ // If an AttackBy returns a status completion we finish our attack
return;
}
}
- //Else check damage component to see if we damage if not attackby, and if so can we attack object
-
- var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clicklocation);
+ var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation);
RaiseEvent(afterAtkMsg);
if (afterAtkMsg.Handled)
- return;
-
- //If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do
- List afterattacks = weapon.GetAllComponents().ToList();
-
- for (var i = 0; i < afterattacks.Count; i++)
{
- afterattacks[i].AfterAttack(new AfterAttackEventArgs { User = user, ClickLocation = clicklocation, Attacked = attacked });
+ return;
+ }
+
+ // If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do
+ var afterAttacks = weapon.GetAllComponents().ToList();
+ var afterAttackEventArgs = new AfterAttackEventArgs
+ {
+ User = user, ClickLocation = clickLocation, Attacked = attacked
+ };
+
+ foreach (var afterAttack in afterAttacks)
+ {
+ afterAttack.AfterAttack(afterAttackEventArgs);
}
}
///
/// Uses an empty hand on an entity
- /// Finds interactable components with the Attackhand interface and calls their function
+ /// Finds components with the AttackHand interface and calls their function
///
- ///
- ///
public void Interaction(IEntity user, IEntity attacked)
{
var message = new AttackHandMessage(user, attacked);
RaiseEvent(message);
- if(message.Handled)
- return;
-
- List interactables = attacked.GetAllComponents().ToList();
-
- for (var i = 0; i < interactables.Count; i++)
+ if (message.Handled)
{
- if (interactables[i].AttackHand(new AttackHandEventArgs { User = user})) //If an attackby returns a status completion we finish our attack
+ return;
+ }
+
+ var attackHands = attacked.GetAllComponents().ToList();
+ var attackHandEventArgs = new AttackHandEventArgs {User = user};
+
+ foreach (var attackHand in attackHands)
+ {
+ if (attackHand.AttackHand(attackHandEventArgs))
{
+ // If an AttackHand returns a status completion we finish our attack
return;
}
}
- //Else check damage component to see if we damage if not attackby, and if so can we attack object
+ // Else we run Activate.
+ InteractionActivate(user, attacked);
}
///
@@ -388,22 +413,23 @@ namespace Content.Server.GameObjects.EntitySystems
/// Activates/Uses an object in control/possession of a user
/// If the item has the IUse interface on one of its components we use the object in our hand
///
- ///
- ///
public void UseInteraction(IEntity user, IEntity used)
{
var useMsg = new UseInHandMessage(user, used);
RaiseEvent(useMsg);
- if(useMsg.Handled)
- return;
-
- List usables = used.GetAllComponents().ToList();
-
- //Try to use item on any components which have the interface
- for (var i = 0; i < usables.Count; i++)
+ if (useMsg.Handled)
{
- if (usables[i].UseEntity(new UseEntityEventArgs { User = user })) //If an attackby returns a status completion we finish our attack
+ return;
+ }
+
+ var uses = used.GetAllComponents().ToList();
+
+ // Try to use item on any components which have the interface
+ foreach (var use in uses)
+ {
+ if (use.UseEntity(new UseEntityEventArgs {User = user}))
{
+ // If a Use returns a status completion we finish our attack
return;
}
}
@@ -413,41 +439,44 @@ namespace Content.Server.GameObjects.EntitySystems
/// Will have two behaviors, either "uses" the weapon at range on the entity if it is capable of accepting that action
/// Or it will use the weapon itself on the position clicked, regardless of what was there
///
- ///
- ///
- ///
public void RangedInteraction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation)
{
var rangedMsg = new RangedAttackMessage(user, weapon, attacked, clickLocation);
RaiseEvent(rangedMsg);
- if(rangedMsg.Handled)
+ if (rangedMsg.Handled)
return;
- List rangedusables = attacked.GetAllComponents().ToList();
-
- //See if we have a ranged attack interaction
- for (var i = 0; i < rangedusables.Count; i++)
+ var rangedAttackBys = attacked.GetAllComponents().ToList();
+ var rangedAttackByEventArgs = new RangedAttackByEventArgs
{
- if (rangedusables[i].RangedAttackBy(new RangedAttackByEventArgs { User = user, Weapon = weapon, ClickLocation = clickLocation })) //If an attackby returns a status completion we finish our attack
+ User = user, Weapon = weapon, ClickLocation = clickLocation
+ };
+
+ // See if we have a ranged attack interaction
+ foreach (var t in rangedAttackBys)
+ {
+ if (t.RangedAttackBy(rangedAttackByEventArgs))
{
+ // If an AttackBy returns a status completion we finish our attack
return;
}
}
- if (weapon != null)
+ var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation);
+ RaiseEvent(afterAtkMsg);
+ if (afterAtkMsg.Handled)
+ return;
+
+ var afterAttacks = weapon.GetAllComponents().ToList();
+ var afterAttackEventArgs = new AfterAttackEventArgs
{
- var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation);
- RaiseEvent(afterAtkMsg);
- if (afterAtkMsg.Handled)
- return;
+ User = user, ClickLocation = clickLocation, Attacked = attacked
+ };
- List afterattacks = weapon.GetAllComponents().ToList();
-
- //See if we have a ranged attack interaction
- for (var i = 0; i < afterattacks.Count; i++)
- {
- afterattacks[i].AfterAttack(new AfterAttackEventArgs { User = user, ClickLocation = clickLocation, Attacked = attacked });
- }
+ //See if we have a ranged attack interaction
+ foreach (var afterAttack in afterAttacks)
+ {
+ afterAttack.AfterAttack(afterAttackEventArgs);
}
}
}
@@ -455,6 +484,7 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Raised when being clicked on or "attacked" by a user with an object in their hand
///
+ [PublicAPI]
public class AttackByMessage : EntitySystemMessage
{
///
@@ -471,7 +501,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// Entity that the User attacked with.
///
public IEntity ItemInHand { get; }
-
+
///
/// Entity that was attacked.
///
@@ -494,6 +524,7 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Raised when being clicked on or "attacked" by a user with an empty hand.
///
+ [PublicAPI]
public class AttackHandMessage : EntitySystemMessage
{
///
@@ -521,6 +552,7 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Raised when being clicked by objects outside the range of direct use.
///
+ [PublicAPI]
public class RangedAttackMessage : EntitySystemMessage
{
///
@@ -560,6 +592,7 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Raised when clicking on another object and no attack event was handled.
///
+ [PublicAPI]
public class AfterAttackMessage : EntitySystemMessage
{
///
@@ -599,6 +632,7 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Raised when using the entity in your hands.
///
+ [PublicAPI]
public class UseInHandMessage : EntitySystemMessage
{
///
@@ -626,6 +660,7 @@ namespace Content.Server.GameObjects.EntitySystems
///
/// Raised when an entity is activated in the world.
///
+ [PublicAPI]
public class ActivateInWorldMessage : EntitySystemMessage
{
///
diff --git a/Content.Server/GameObjects/EntitySystems/StorageSystem.cs b/Content.Server/GameObjects/EntitySystems/StorageSystem.cs
index 1cbc59cd6c..cc54bf3d46 100644
--- a/Content.Server/GameObjects/EntitySystems/StorageSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/StorageSystem.cs
@@ -81,7 +81,7 @@ namespace Content.Server.GameObjects.EntitySystems
continue;
var distanceSquared = (storagePos - attachedEntity.Transform.WorldPosition).LengthSquared;
- if (distanceSquared > InteractionSystem.INTERACTION_RANGE_SQUARED)
+ if (distanceSquared > InteractionSystem.InteractionRangeSquared)
{
storageComp.UnsubscribeSession(session);
}
diff --git a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs
new file mode 100644
index 0000000000..fa9d4ae76b
--- /dev/null
+++ b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs
@@ -0,0 +1,10 @@
+namespace Content.Server.GameTicking.GamePresets
+{
+ public sealed class PresetSandbox : GamePreset
+ {
+ public override void Start()
+ {
+ // Nothing yet.
+ }
+ }
+}
diff --git a/Content.Server/GameTicking/GamePresets/PresetTraitor.cs b/Content.Server/GameTicking/GamePresets/PresetTraitor.cs
deleted file mode 100644
index 787cf8a146..0000000000
--- a/Content.Server/GameTicking/GamePresets/PresetTraitor.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Robust.Shared.Log;
-
-namespace Content.Server.GameTicking.GamePresets
-{
- public class PresetTraitor : GamePreset
- {
- public override void Start()
- {
- Logger.DebugS("ticker.preset", "Current preset is traitor.");
- }
- }
-}
diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs
index 5b98912752..520331945c 100644
--- a/Content.Server/GameTicking/GameTicker.cs
+++ b/Content.Server/GameTicking/GameTicker.cs
@@ -15,13 +15,13 @@ using Robust.Server.Interfaces.Maps;
using Robust.Server.Interfaces.Player;
using Robust.Server.Player;
using Robust.Shared.Configuration;
-using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
+using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -81,6 +81,8 @@ namespace Content.Server.GameTicking
[ViewVariables] private readonly List _gameRules = new List();
+ [ViewVariables] private Type _presetType;
+
#pragma warning disable 649
[Dependency] private IEntityManager _entityManager;
[Dependency] private IMapManager _mapManager;
@@ -155,7 +157,7 @@ namespace Content.Server.GameTicking
RunLevel = GameRunLevel.InRound;
// TODO: Allow other presets to be selected.
- var preset = _dynamicTypeFactory.CreateInstance();
+ var preset = (GamePreset)_dynamicTypeFactory.CreateInstance(_presetType ?? typeof(PresetSandbox));
preset.Start();
foreach (var (playerSession, ready) in _playersInLobby.ToList())
@@ -248,6 +250,15 @@ namespace Content.Server.GameTicking
public IEnumerable ActiveGameRules => _gameRules;
+ public void SetStartPreset(Type type)
+ {
+ if (!typeof(GamePreset).IsAssignableFrom(type))
+ {
+ throw new ArgumentException("type must inherit GamePreset");
+ }
+ _presetType = type;
+ }
+
private IEntity _spawnPlayerMob()
{
var entity = _entityManager.ForceSpawnEntityAt(PlayerPrototypeName, _getLateJoinSpawnPoint());
@@ -643,4 +654,38 @@ namespace Content.Server.GameTicking
ticker.ToggleReady(player, bool.Parse(args[0]));
}
}
+
+ class SetGamePresetCommand : IClientCommand
+ {
+ public string Command => "setgamepreset";
+ public string Description => "";
+ public string Help => "";
+
+ public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
+ {
+ if (args.Length != 1)
+ {
+ shell.SendText(player, "Need exactly one argument.");
+ return;
+ }
+
+ var ticker = IoCManager.Resolve();
+
+ Type presetType;
+ switch (args[0])
+ {
+ case "DeathMatch":
+ presetType = typeof(PresetDeathMatch);
+ break;
+ case "Sandbox":
+ presetType = typeof(PresetSandbox);
+ break;
+ default:
+ shell.SendText(player, "That is not a valid game preset!");
+ return;
+ }
+
+ ticker.SetStartPreset(presetType);
+ }
+ }
}
diff --git a/Content.Server/Interfaces/GameTicking/IGameTicker.cs b/Content.Server/Interfaces/GameTicking/IGameTicker.cs
index ba737643c4..cf879f7c32 100644
--- a/Content.Server/Interfaces/GameTicking/IGameTicker.cs
+++ b/Content.Server/Interfaces/GameTicking/IGameTicker.cs
@@ -33,5 +33,7 @@ namespace Content.Server.Interfaces.GameTicking
T AddGameRule() where T : GameRule, new();
void RemoveGameRule(GameRule rule);
IEnumerable ActiveGameRules { get; }
+
+ void SetStartPreset(Type type);
}
}
diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml
index 1df54bd807..2aecb2f4ee 100644
--- a/Resources/Groups/groups.yml
+++ b/Resources/Groups/groups.yml
@@ -45,4 +45,9 @@
- delete
- tp
- tpgrid
+ - setgamepreset
+ - startround
+ - endround
+ - restartround
+ - respawn
CanViewVar: true
diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml
index 6dce1d0f63..b0b87c8a81 100644
--- a/Resources/keybinds.yml
+++ b/Resources/keybinds.yml
@@ -45,13 +45,13 @@ binds:
key: MouseMiddle
type: State
- function: SwapHands
- key: Tab
+ key: X
type: State
- function: Drop
key: Q
type: State
- function: ActivateItemInHand
- key: F
+ key: Z
type: State
- function: OpenCharacterMenu
key: C
diff --git a/RobustToolbox b/RobustToolbox
index 3bad55a705..10440648bc 160000
--- a/RobustToolbox
+++ b/RobustToolbox
@@ -1 +1 @@
-Subproject commit 3bad55a705b000e691bdcf50e54948f59fcc51bc
+Subproject commit 10440648bc1225cecb5eb09ec2c45d0ec18423c9