Merge branch 'master' into no-tscn

This commit is contained in:
moneyl
2019-05-19 02:14:24 -04:00
23 changed files with 359 additions and 207 deletions

View File

@@ -127,6 +127,7 @@
<Compile Include="UserInterface\LobbyGui.cs" /> <Compile Include="UserInterface\LobbyGui.cs" />
<Compile Include="UserInterface\NanoStyle.cs" /> <Compile Include="UserInterface\NanoStyle.cs" />
<Compile Include="UserInterface\Placeholder.cs" /> <Compile Include="UserInterface\Placeholder.cs" />
<Compile Include="UserInterface\TutorialButton.cs" />
<Compile Include="Utility\ResourceCacheExtensions.cs" /> <Compile Include="Utility\ResourceCacheExtensions.cs" />
<Compile Include="GameObjects\Components\Mobs\SpeciesVisualizer2D.cs" /> <Compile Include="GameObjects\Components\Mobs\SpeciesVisualizer2D.cs" />
</ItemGroup> </ItemGroup>

View File

@@ -1,12 +1,10 @@
using Content.Client.UserInterface; using Content.Client.UserInterface;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Input; using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.Placement; using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State; using Robust.Client.Interfaces.State;
using Robust.Client.State.States; using Robust.Client.State.States;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
@@ -19,7 +17,6 @@ namespace Content.Client
{ {
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IStateManager _stateManager; [Dependency] private readonly IStateManager _stateManager;
[Dependency] private readonly IDisplayManager _displayManager;
[Dependency] private readonly IClientConsole _clientConsole; [Dependency] private readonly IClientConsole _clientConsole;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
[Dependency] private readonly IPlacementManager _placementManager; [Dependency] private readonly IPlacementManager _placementManager;
@@ -41,7 +38,7 @@ namespace Content.Client
if (obj.NewState is GameScreen) if (obj.NewState is GameScreen)
{ {
// Switched TO GameScreen. // Switched TO GameScreen.
_escapeMenu = new EscapeMenu(_displayManager, _clientConsole, _tileDefinitionManager, _placementManager, _escapeMenu = new EscapeMenu(_clientConsole, _tileDefinitionManager, _placementManager,
_prototypeManager, _resourceCache, _configurationManager) _prototypeManager, _resourceCache, _configurationManager)
{ {
Visible = false Visible = false

View File

@@ -44,6 +44,7 @@ namespace Content.Client.GameTicking
[ViewVariables] private LobbyGui _lobby; [ViewVariables] private LobbyGui _lobby;
[ViewVariables] private bool _gameStarted; [ViewVariables] private bool _gameStarted;
[ViewVariables] private DateTime _startTime; [ViewVariables] private DateTime _startTime;
[ViewVariables] private TutorialButton _tutorialButton;
public void Initialize() public void Initialize()
{ {
@@ -66,6 +67,11 @@ namespace Content.Client.GameTicking
return; return;
} }
_updatePlayerList();
}
private void _updatePlayerList()
{
_lobby.OnlinePlayerItemList.Clear(); _lobby.OnlinePlayerItemList.Clear();
foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name)) foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name))
{ {
@@ -165,6 +171,12 @@ namespace Content.Client.GameTicking
_gameChat = null; _gameChat = null;
} }
if (_tutorialButton != null)
{
_tutorialButton.Dispose();
_tutorialButton = null;
}
_tickerState = TickerState.InLobby; _tickerState = TickerState.InLobby;
_lobby = new LobbyGui(_localization, _resourceCache); _lobby = new LobbyGui(_localization, _resourceCache);
@@ -208,6 +220,8 @@ namespace Content.Client.GameTicking
}; };
_lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect"); _lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect");
_updatePlayerList();
} }
private void _joinGame(MsgTickerJoinGame message) private void _joinGame(MsgTickerJoinGame message)
@@ -235,6 +249,9 @@ namespace Content.Client.GameTicking
_gameChat = new ChatBox(); _gameChat = new ChatBox();
_userInterfaceManager.StateRoot.AddChild(_gameChat); _userInterfaceManager.StateRoot.AddChild(_gameChat);
_chatManager.SetChatBox(_gameChat); _chatManager.SetChatBox(_gameChat);
_tutorialButton = new TutorialButton();
_userInterfaceManager.StateRoot.AddChild(_tutorialButton);
_tutorialButton.SetAnchorAndMarginPreset(Control.LayoutPreset.BottomLeft, Control.LayoutPresetMode.MinSize, 50);
_gameChat.DefaultChatFormat = "say \"{0}\""; _gameChat.DefaultChatFormat = "say \"{0}\"";
} }

View File

@@ -18,7 +18,6 @@ namespace Content.Client.UserInterface
private readonly IPlacementManager _placementManager; private readonly IPlacementManager _placementManager;
private readonly IPrototypeManager _prototypeManager; private readonly IPrototypeManager _prototypeManager;
private readonly IResourceCache _resourceCache; private readonly IResourceCache _resourceCache;
private readonly IDisplayManager _displayManager;
private readonly IConfigurationManager _configSystem; private readonly IConfigurationManager _configSystem;
private BaseButton QuitButton; private BaseButton QuitButton;
@@ -27,16 +26,14 @@ namespace Content.Client.UserInterface
private BaseButton SpawnTilesButton; private BaseButton SpawnTilesButton;
private OptionsMenu optionsMenu; private OptionsMenu optionsMenu;
public EscapeMenu(IDisplayManager displayManager, public EscapeMenu(IClientConsole console,
IClientConsole console,
ITileDefinitionManager tileDefinitionManager, ITileDefinitionManager tileDefinitionManager,
IPlacementManager placementManager, IPlacementManager placementManager,
IPrototypeManager prototypeManager, IPrototypeManager prototypeManager,
IResourceCache resourceCache, IResourceCache resourceCache,
IConfigurationManager configSystem) : base(displayManager) IConfigurationManager configSystem)
{ {
_configSystem = configSystem; _configSystem = configSystem;
_displayManager = displayManager;
_console = console; _console = console;
__tileDefinitionManager = tileDefinitionManager; __tileDefinitionManager = tileDefinitionManager;
_placementManager = placementManager; _placementManager = placementManager;
@@ -48,7 +45,7 @@ namespace Content.Client.UserInterface
private void PerformLayout() private void PerformLayout()
{ {
optionsMenu = new OptionsMenu(_displayManager, _configSystem) optionsMenu = new OptionsMenu(_configSystem)
{ {
Visible = false Visible = false
}; };
@@ -97,14 +94,14 @@ namespace Content.Client.UserInterface
private void OnSpawnEntitiesButtonClicked(BaseButton.ButtonEventArgs args) private void OnSpawnEntitiesButtonClicked(BaseButton.ButtonEventArgs args)
{ {
var window = new EntitySpawnWindow(_displayManager, _placementManager, _prototypeManager, _resourceCache); var window = new EntitySpawnWindow(_placementManager, _prototypeManager, _resourceCache);
window.AddToScreen(); window.AddToScreen();
window.OpenToLeft(); window.OpenToLeft();
} }
private void OnSpawnTilesButtonClicked(BaseButton.ButtonEventArgs args) private void OnSpawnTilesButtonClicked(BaseButton.ButtonEventArgs args)
{ {
var window = new TileSpawnWindow(__tileDefinitionManager, _placementManager, _displayManager, _resourceCache); var window = new TileSpawnWindow(__tileDefinitionManager, _placementManager, _resourceCache);
window.AddToScreen(); window.AddToScreen();
window.OpenToLeft(); window.OpenToLeft();
} }

View File

@@ -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();
}
}
}

View File

@@ -142,7 +142,7 @@
<Compile Include="GameObjects\EntitySystems\TemperatureSystem.cs" /> <Compile Include="GameObjects\EntitySystems\TemperatureSystem.cs" />
<Compile Include="GameTicking\GamePreset.cs" /> <Compile Include="GameTicking\GamePreset.cs" />
<Compile Include="GameTicking\GamePresets\PresetDeathMatch.cs" /> <Compile Include="GameTicking\GamePresets\PresetDeathMatch.cs" />
<Compile Include="GameTicking\GamePresets\PresetTraitor.cs" /> <Compile Include="GameTicking\GamePresets\PresetSandbox.cs" />
<Compile Include="GameTicking\GameRule.cs" /> <Compile Include="GameTicking\GameRule.cs" />
<Compile Include="GameTicking\GameRules\RuleDeathMatch.cs" /> <Compile Include="GameTicking\GameRules\RuleDeathMatch.cs" />
<Compile Include="GameTicking\GameTicker.cs" /> <Compile Include="GameTicking\GameTicker.cs" />

View File

@@ -88,6 +88,7 @@ namespace Content.Server
factory.Register<DestructibleComponent>(); factory.Register<DestructibleComponent>();
factory.Register<TemperatureComponent>(); factory.Register<TemperatureComponent>();
factory.Register<ServerDoorComponent>(); factory.Register<ServerDoorComponent>();
factory.RegisterReference<ServerDoorComponent, IActivate>();
//Power Components //Power Components
factory.Register<PowerTransferComponent>(); factory.Register<PowerTransferComponent>();
@@ -127,6 +128,7 @@ namespace Content.Server
factory.RegisterReference<ServerStorageComponent, IActivate>(); factory.RegisterReference<ServerStorageComponent, IActivate>();
factory.Register<EntityStorageComponent>(); factory.Register<EntityStorageComponent>();
factory.RegisterReference<EntityStorageComponent, IStorageComponent>(); factory.RegisterReference<EntityStorageComponent, IStorageComponent>();
factory.RegisterReference<EntityStorageComponent, IActivate>();
factory.Register<ToolLockerFillComponent>(); factory.Register<ToolLockerFillComponent>();
factory.Register<ToolboxElectricalFillComponent>(); factory.Register<ToolboxElectricalFillComponent>();
@@ -135,6 +137,7 @@ namespace Content.Server
factory.Register<PoweredLightComponent>(); factory.Register<PoweredLightComponent>();
factory.Register<SmesComponent>(); factory.Register<SmesComponent>();
factory.Register<ApcComponent>(); factory.Register<ApcComponent>();
factory.RegisterReference<ApcComponent, IActivate>();
factory.Register<MaterialComponent>(); factory.Register<MaterialComponent>();
factory.Register<StackComponent>(); factory.Register<StackComponent>();
factory.Register<MaterialStorageComponent>(); factory.Register<MaterialStorageComponent>();
@@ -152,6 +155,7 @@ namespace Content.Server
factory.RegisterReference<SpawnPointComponent, SharedSpawnPointComponent>(); factory.RegisterReference<SpawnPointComponent, SharedSpawnPointComponent>();
factory.Register<LatheComponent>(); factory.Register<LatheComponent>();
factory.RegisterReference<LatheComponent, IActivate>();
factory.Register<LatheDatabaseComponent>(); factory.Register<LatheDatabaseComponent>();
factory.RegisterReference<LatheDatabaseComponent, SharedLatheDatabaseComponent>(); factory.RegisterReference<LatheDatabaseComponent, SharedLatheDatabaseComponent>();

View File

@@ -93,6 +93,10 @@ namespace Content.Server.GameObjects.Components.Construction
bool TryProcessStep(ConstructionStep step, IEntity slapped) bool TryProcessStep(ConstructionStep step, IEntity slapped)
{ {
if (step == null)
{
return false;
}
var sound = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>(); var sound = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
switch (step) switch (step)

View File

@@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Construction
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName); var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
var transform = Owner.Transform; var transform = Owner.Transform;
if (!loc.InRange(_mapManager, transform.GridPosition, InteractionSystem.INTERACTION_RANGE)) if (!loc.InRange(_mapManager, transform.GridPosition, InteractionSystem.InteractionRange))
{ {
return; return;
} }

View File

@@ -10,7 +10,7 @@ using Robust.Shared.Timers;
namespace Content.Server.GameObjects namespace Content.Server.GameObjects
{ {
public class ServerDoorComponent : Component, IAttackHand public class ServerDoorComponent : Component, IActivate
{ {
public override string Name => "Door"; public override string Name => "Door";
@@ -41,7 +41,7 @@ namespace Content.Server.GameObjects
base.OnRemove(); base.OnRemove();
} }
public bool AttackHand(AttackHandEventArgs eventArgs) void IActivate.Activate(ActivateEventArgs eventArgs)
{ {
if (_state == DoorState.Open) if (_state == DoorState.Open)
{ {
@@ -51,7 +51,6 @@ namespace Content.Server.GameObjects
{ {
Open(); Open();
} }
return true;
} }
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)

View File

@@ -462,8 +462,8 @@ namespace Content.Server.GameObjects
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 && slot.ContainedEntity != null)
{ {
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>(); var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
interactionSystem.Interaction(Owner, used, slot.ContainedEntity, interactionSystem.Interaction(Owner, used, slot.ContainedEntity,
GridCoordinates.Nullspace); GridCoordinates.Nullspace);

View File

@@ -14,7 +14,7 @@ using Robust.Server.GameObjects;
namespace Content.Server.GameObjects.Components namespace Content.Server.GameObjects.Components
{ {
public class EntityStorageComponent : Component, IAttackHand, IStorageComponent public class EntityStorageComponent : Component, IActivate, IStorageComponent
{ {
public override string Name => "EntityStorage"; public override string Name => "EntityStorage";
@@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components
[ViewVariables] [ViewVariables]
public bool Open { get; private set; } public bool Open { get; private set; }
public bool AttackHand(AttackHandEventArgs eventArgs) void IActivate.Activate(ActivateEventArgs eventArgs)
{ {
if (Open) if (Open)
{ {
@@ -51,7 +51,6 @@ namespace Content.Server.GameObjects.Components
{ {
OpenStorage(); OpenStorage();
} }
return true;
} }
private void CloseStorage() private void CloseStorage()

View File

@@ -12,7 +12,7 @@ using Robust.Shared.IoC;
namespace Content.Server.GameObjects.Components.Power namespace Content.Server.GameObjects.Components.Power
{ {
public sealed class ApcComponent : SharedApcComponent, IAttackHand public sealed class ApcComponent : SharedApcComponent, IActivate
{ {
PowerStorageComponent Storage; PowerStorageComponent Storage;
AppearanceComponent Appearance; AppearanceComponent Appearance;
@@ -106,15 +106,14 @@ namespace Content.Server.GameObjects.Components.Power
return net.Lack > 0 ? ApcExternalPowerState.Low : ApcExternalPowerState.Good; 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)) if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
{ {
return false; return;
} }
_userInterface.Open(actor.playerSession); _userInterface.Open(actor.playerSession);
return true;
} }
private void _clickSound() private void _clickSound()

View File

@@ -15,7 +15,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Research namespace Content.Server.GameObjects.Components.Research
{ {
public class LatheComponent : SharedLatheComponent, IAttackHand, IAttackBy, IActivate public class LatheComponent : SharedLatheComponent, IAttackBy, IActivate
{ {
public const int VolumePerSheet = 3750; public const int VolumePerSheet = 3750;
@@ -94,17 +94,6 @@ namespace Content.Server.GameObjects.Components.Research
_userInterface.Open(actor.playerSession); _userInterface.Open(actor.playerSession);
return; 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) bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
{ {
if (!Owner.TryGetComponent(out MaterialStorageComponent storage) if (!Owner.TryGetComponent(out MaterialStorageComponent storage)

View File

@@ -1,19 +1,19 @@
using System; 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 System.Linq;
using Content.Server.Interfaces.GameObjects;
using Content.Shared.Input; using Content.Shared.Input;
using Robust.Shared.Input; using JetBrains.Annotations;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player; 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.GameObjects.Components;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Players; using Robust.Shared.Players;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems
@@ -26,9 +26,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Called when using one object on another /// Called when using one object on another
/// </summary> /// </summary>
/// <param name="user"></param>
/// <param name="attackwith"></param>
/// <returns></returns>
bool AttackBy(AttackByEventArgs eventArgs); bool AttackBy(AttackByEventArgs eventArgs);
} }
@@ -47,8 +44,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Called when a player directly interacts with an empty hand /// Called when a player directly interacts with an empty hand
/// </summary> /// </summary>
/// <param name="user"></param>
/// <returns></returns>
bool AttackHand(AttackHandEventArgs eventArgs); bool AttackHand(AttackHandEventArgs eventArgs);
} }
@@ -65,13 +60,11 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Called when we try to interact with an entity out of range /// Called when we try to interact with an entity out of range
/// </summary> /// </summary>
/// <param name="user"></param>
/// <param name="attackwith"></param>
/// <param name="clicklocation"></param>
/// <returns></returns> /// <returns></returns>
bool RangedAttackBy(RangedAttackByEventArgs eventArgs); bool RangedAttackBy(RangedAttackByEventArgs eventArgs);
} }
[PublicAPI]
public class RangedAttackByEventArgs : EventArgs public class RangedAttackByEventArgs : EventArgs
{ {
public IEntity User { get; set; } public IEntity User { get; set; }
@@ -88,9 +81,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior /// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior
/// </summary> /// </summary>
/// <param name="user"></param>
/// <param name="clicklocation"></param>
/// <param name="attacked">The entity that was clicked on out of range. May be null if no entity was clicked on.true</param>
void AfterAttack(AfterAttackEventArgs eventArgs); void AfterAttack(AfterAttackEventArgs eventArgs);
} }
@@ -109,7 +99,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Called when we activate an object we are holding to use it /// Called when we activate an object we are holding to use it
/// </summary> /// </summary>
/// <param name="user"></param>
/// <returns></returns> /// <returns></returns>
bool UseEntity(UseEntityEventArgs eventArgs); bool UseEntity(UseEntityEventArgs eventArgs);
} }
@@ -127,7 +116,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Called when this component is activated by another entity. /// Called when this component is activated by another entity.
/// </summary> /// </summary>
/// <param name="user">Entity that activated this component.</param>
void Activate(ActivateEventArgs eventArgs); void Activate(ActivateEventArgs eventArgs);
} }
@@ -139,50 +127,66 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Governs interactions during clicking on entities /// Governs interactions during clicking on entities
/// </summary> /// </summary>
public class InteractionSystem : EntitySystem [UsedImplicitly]
public sealed class InteractionSystem : EntitySystem
{ {
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649 #pragma warning restore 649
public const float INTERACTION_RANGE = 2; public const float InteractionRange = 2;
public const float INTERACTION_RANGE_SQUARED = INTERACTION_RANGE * INTERACTION_RANGE; public const float InteractionRangeSquared = InteractionRange * InteractionRange;
public override void Initialize() public override void Initialize()
{ {
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>(); var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(ContentKeyFunctions.UseItemInHand, new PointerInputCmdHandler(HandleUseItemInHand)); inputSys.BindMap.BindFunction(ContentKeyFunctions.UseItemInHand,
inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld, new PointerInputCmdHandler((HandleUseItemInWorld))); 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; return;
var playerEnt = ((IPlayerSession) session).AttachedEntity; var playerEnt = ((IPlayerSession) session).AttachedEntity;
if(playerEnt == null || !playerEnt.IsValid()) if (playerEnt == null || !playerEnt.IsValid())
{
return; return;
}
if (!playerEnt.Transform.GridPosition.InRange(_mapManager, used.Transform.GridPosition, INTERACTION_RANGE)) if (!playerEnt.Transform.GridPosition.InRange(_mapManager, used.Transform.GridPosition, InteractionRange))
{
return; 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); RaiseEvent(activateMsg);
if(activateMsg.Handled) if (activateMsg.Handled)
{
return; return;
}
if (!used.TryGetComponent(out IActivate activateComp)) if (!used.TryGetComponent(out IActivate activateComp))
{
return; return;
}
activateComp.Activate(new ActivateEventArgs { User = playerEnt }); activateComp.Activate(new ActivateEventArgs {User = user});
} }
private void HandleUseItemInHand(ICommonSession session, GridCoordinates coords, EntityUid uid) private void HandleUseItemInHand(ICommonSession session, GridCoordinates coords, EntityUid uid)
{ {
// client sanitization // client sanitization
if(!_mapManager.GridExists(coords.GridID)) if (!_mapManager.GridExists(coords.GridID))
{ {
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}"); Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return; return;
@@ -190,32 +194,37 @@ namespace Content.Server.GameObjects.EntitySystems
if (uid.IsClientSide()) 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; return;
} }
UserInteraction(((IPlayerSession)session).AttachedEntity, coords, uid); UserInteraction(((IPlayerSession) session).AttachedEntity, coords, uid);
} }
private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid) 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)) if (!EntityManager.TryGetEntity(clickedUid, out var attacked))
{
attacked = null; attacked = null;
}
//Verify player has a transform component // Verify player has a transform component
if (!player.TryGetComponent<ITransformComponent>(out var playerTransform)) if (!player.TryGetComponent<ITransformComponent>(out var playerTransform))
{ {
return; 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; 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<IHandsComponent>(out var hands)) if (!player.TryGetComponent<IHandsComponent>(out var hands))
{ {
return; return;
@@ -224,59 +233,64 @@ namespace Content.Server.GameObjects.EntitySystems
var item = hands.GetActiveHand?.Owner; var item = hands.GetActiveHand?.Owner;
if (!ActionBlockerSystem.CanInteract(player)) 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; return;
} }
//Verify attacked object is on the map if we managed to click on it somehow // TODO: Check if client should be able to see that object to click on it in the first place
if (!attacked.GetComponent<ITransformComponent>().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;
}
//Check if ClickLocation is in object bounds here, if not lets log as warning and see why // Clicked on empty space behavior, try using ranged attack
if (attacked.TryGetComponent(out BoundingBoxComponent boundingbox)) 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; return;
} }
} }
//RANGEDATTACK/AFTERATTACK: Check distance between user and clicked item, if too large parse it in the ranged function // 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? // 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<ITransformComponent>().WorldPosition).LengthSquared; var distance = (playerTransform.WorldPosition - attacked.Transform.WorldPosition).LengthSquared;
if (distance > INTERACTION_RANGE_SQUARED) if (distance > InteractionRangeSquared)
{ {
if (item != null) if (item != null)
{ {
RangedInteraction(player, item, attacked, coordinates); RangedInteraction(player, item, attacked, coordinates);
return; 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 // 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 // AttackBy/AfterAttack: We will either use the item on the nearby object
if (item != null) if (item != null)
{ {
Interaction(player, item, attacked, coordinates); 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 else
{ {
Interaction(player, attacked); Interaction(player, attacked);
@@ -284,90 +298,101 @@ namespace Content.Server.GameObjects.EntitySystems
} }
/// <summary> /// <summary>
/// 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
/// </summary> /// </summary>
/// <param name="user"></param> private void InteractAfterAttack(IEntity user, IEntity weapon, GridCoordinates clickLocation)
/// <param name="weapon"></param>
/// <param name="clicklocation"></param>
public 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); RaiseEvent(message);
if(message.Handled) if (message.Handled)
return;
List<IAfterAttack> afterattacks = weapon.GetAllComponents<IAfterAttack>().ToList();
for (var i = 0; i < afterattacks.Count; i++)
{ {
afterattacks[i].AfterAttack(new AfterAttackEventArgs { User = user, ClickLocation = clicklocation }); return;
}
var afterAttacks = weapon.GetAllComponents<IAfterAttack>().ToList();
var afterAttackEventArgs = new AfterAttackEventArgs {User = user, ClickLocation = clickLocation};
foreach (var afterAttack in afterAttacks)
{
afterAttack.AfterAttack(afterAttackEventArgs);
} }
} }
/// <summary> /// <summary>
/// Uses a weapon/object on an entity /// 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
/// </summary> /// </summary>
/// <param name="user"></param> public void Interaction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation)
/// <param name="weapon"></param>
/// <param name="attacked"></param>
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); RaiseEvent(attackMsg);
if(attackMsg.Handled) if (attackMsg.Handled)
return;
List<IAttackBy> interactables = attacked.GetAllComponents<IAttackBy>().ToList();
for (var i = 0; i < interactables.Count; i++)
{ {
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<IAttackBy>().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; 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); RaiseEvent(afterAtkMsg);
if (afterAtkMsg.Handled) 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<IAfterAttack> afterattacks = weapon.GetAllComponents<IAfterAttack>().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<IAfterAttack>().ToList();
var afterAttackEventArgs = new AfterAttackEventArgs
{
User = user, ClickLocation = clickLocation, Attacked = attacked
};
foreach (var afterAttack in afterAttacks)
{
afterAttack.AfterAttack(afterAttackEventArgs);
} }
} }
/// <summary> /// <summary>
/// Uses an empty hand on an entity /// 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
/// </summary> /// </summary>
/// <param name="user"></param>
/// <param name="attacked"></param>
public void Interaction(IEntity user, IEntity attacked) public void Interaction(IEntity user, IEntity attacked)
{ {
var message = new AttackHandMessage(user, attacked); var message = new AttackHandMessage(user, attacked);
RaiseEvent(message); RaiseEvent(message);
if(message.Handled) if (message.Handled)
return;
List<IAttackHand> interactables = attacked.GetAllComponents<IAttackHand>().ToList();
for (var i = 0; i < interactables.Count; i++)
{ {
if (interactables[i].AttackHand(new AttackHandEventArgs { User = user})) //If an attackby returns a status completion we finish our attack return;
}
var attackHands = attacked.GetAllComponents<IAttackHand>().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; 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);
} }
/// <summary> /// <summary>
@@ -388,22 +413,23 @@ namespace Content.Server.GameObjects.EntitySystems
/// Activates/Uses an object in control/possession of a user /// 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 /// If the item has the IUse interface on one of its components we use the object in our hand
/// </summary> /// </summary>
/// <param name="user"></param>
/// <param name="attacked"></param>
public void UseInteraction(IEntity user, IEntity used) public void UseInteraction(IEntity user, IEntity used)
{ {
var useMsg = new UseInHandMessage(user, used); var useMsg = new UseInHandMessage(user, used);
RaiseEvent(useMsg); RaiseEvent(useMsg);
if(useMsg.Handled) if (useMsg.Handled)
return;
List<IUse> usables = used.GetAllComponents<IUse>().ToList();
//Try to use item on any components which have the interface
for (var i = 0; i < usables.Count; i++)
{ {
if (usables[i].UseEntity(new UseEntityEventArgs { User = user })) //If an attackby returns a status completion we finish our attack return;
}
var uses = used.GetAllComponents<IUse>().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; 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 /// 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 /// Or it will use the weapon itself on the position clicked, regardless of what was there
/// </summary> /// </summary>
/// <param name="user"></param>
/// <param name="weapon"></param>
/// <param name="attacked"></param>
public void RangedInteraction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation) public void RangedInteraction(IEntity user, IEntity weapon, IEntity attacked, GridCoordinates clickLocation)
{ {
var rangedMsg = new RangedAttackMessage(user, weapon, attacked, clickLocation); var rangedMsg = new RangedAttackMessage(user, weapon, attacked, clickLocation);
RaiseEvent(rangedMsg); RaiseEvent(rangedMsg);
if(rangedMsg.Handled) if (rangedMsg.Handled)
return; return;
List<IRangedAttackBy> rangedusables = attacked.GetAllComponents<IRangedAttackBy>().ToList(); var rangedAttackBys = attacked.GetAllComponents<IRangedAttackBy>().ToList();
var rangedAttackByEventArgs = new RangedAttackByEventArgs
//See if we have a ranged attack interaction
for (var i = 0; i < rangedusables.Count; i++)
{ {
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; return;
} }
} }
if (weapon != null) var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation);
RaiseEvent(afterAtkMsg);
if (afterAtkMsg.Handled)
return;
var afterAttacks = weapon.GetAllComponents<IAfterAttack>().ToList();
var afterAttackEventArgs = new AfterAttackEventArgs
{ {
var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation); User = user, ClickLocation = clickLocation, Attacked = attacked
RaiseEvent(afterAtkMsg); };
if (afterAtkMsg.Handled)
return;
List<IAfterAttack> afterattacks = weapon.GetAllComponents<IAfterAttack>().ToList(); //See if we have a ranged attack interaction
foreach (var afterAttack in afterAttacks)
//See if we have a ranged attack interaction {
for (var i = 0; i < afterattacks.Count; i++) afterAttack.AfterAttack(afterAttackEventArgs);
{
afterattacks[i].AfterAttack(new AfterAttackEventArgs { User = user, ClickLocation = clickLocation, Attacked = attacked });
}
} }
} }
} }
@@ -455,6 +484,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Raised when being clicked on or "attacked" by a user with an object in their hand /// Raised when being clicked on or "attacked" by a user with an object in their hand
/// </summary> /// </summary>
[PublicAPI]
public class AttackByMessage : EntitySystemMessage public class AttackByMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
@@ -471,7 +501,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// Entity that the User attacked with. /// Entity that the User attacked with.
/// </summary> /// </summary>
public IEntity ItemInHand { get; } public IEntity ItemInHand { get; }
/// <summary> /// <summary>
/// Entity that was attacked. /// Entity that was attacked.
/// </summary> /// </summary>
@@ -494,6 +524,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Raised when being clicked on or "attacked" by a user with an empty hand. /// Raised when being clicked on or "attacked" by a user with an empty hand.
/// </summary> /// </summary>
[PublicAPI]
public class AttackHandMessage : EntitySystemMessage public class AttackHandMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
@@ -521,6 +552,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Raised when being clicked by objects outside the range of direct use. /// Raised when being clicked by objects outside the range of direct use.
/// </summary> /// </summary>
[PublicAPI]
public class RangedAttackMessage : EntitySystemMessage public class RangedAttackMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
@@ -560,6 +592,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Raised when clicking on another object and no attack event was handled. /// Raised when clicking on another object and no attack event was handled.
/// </summary> /// </summary>
[PublicAPI]
public class AfterAttackMessage : EntitySystemMessage public class AfterAttackMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
@@ -599,6 +632,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Raised when using the entity in your hands. /// Raised when using the entity in your hands.
/// </summary> /// </summary>
[PublicAPI]
public class UseInHandMessage : EntitySystemMessage public class UseInHandMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
@@ -626,6 +660,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary> /// <summary>
/// Raised when an entity is activated in the world. /// Raised when an entity is activated in the world.
/// </summary> /// </summary>
[PublicAPI]
public class ActivateInWorldMessage : EntitySystemMessage public class ActivateInWorldMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>

View File

@@ -81,7 +81,7 @@ namespace Content.Server.GameObjects.EntitySystems
continue; continue;
var distanceSquared = (storagePos - attachedEntity.Transform.WorldPosition).LengthSquared; var distanceSquared = (storagePos - attachedEntity.Transform.WorldPosition).LengthSquared;
if (distanceSquared > InteractionSystem.INTERACTION_RANGE_SQUARED) if (distanceSquared > InteractionSystem.InteractionRangeSquared)
{ {
storageComp.UnsubscribeSession(session); storageComp.UnsubscribeSession(session);
} }

View File

@@ -0,0 +1,10 @@
namespace Content.Server.GameTicking.GamePresets
{
public sealed class PresetSandbox : GamePreset
{
public override void Start()
{
// Nothing yet.
}
}
}

View File

@@ -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.");
}
}
}

View File

@@ -15,13 +15,13 @@ using Robust.Server.Interfaces.Maps;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network; using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Timing; using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
@@ -81,6 +81,8 @@ namespace Content.Server.GameTicking
[ViewVariables] private readonly List<GameRule> _gameRules = new List<GameRule>(); [ViewVariables] private readonly List<GameRule> _gameRules = new List<GameRule>();
[ViewVariables] private Type _presetType;
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private IEntityManager _entityManager; [Dependency] private IEntityManager _entityManager;
[Dependency] private IMapManager _mapManager; [Dependency] private IMapManager _mapManager;
@@ -155,7 +157,7 @@ namespace Content.Server.GameTicking
RunLevel = GameRunLevel.InRound; RunLevel = GameRunLevel.InRound;
// TODO: Allow other presets to be selected. // TODO: Allow other presets to be selected.
var preset = _dynamicTypeFactory.CreateInstance<PresetTraitor>(); var preset = (GamePreset)_dynamicTypeFactory.CreateInstance(_presetType ?? typeof(PresetSandbox));
preset.Start(); preset.Start();
foreach (var (playerSession, ready) in _playersInLobby.ToList()) foreach (var (playerSession, ready) in _playersInLobby.ToList())
@@ -248,6 +250,15 @@ namespace Content.Server.GameTicking
public IEnumerable<GameRule> ActiveGameRules => _gameRules; public IEnumerable<GameRule> ActiveGameRules => _gameRules;
public void SetStartPreset(Type type)
{
if (!typeof(GamePreset).IsAssignableFrom(type))
{
throw new ArgumentException("type must inherit GamePreset");
}
_presetType = type;
}
private IEntity _spawnPlayerMob() private IEntity _spawnPlayerMob()
{ {
var entity = _entityManager.ForceSpawnEntityAt(PlayerPrototypeName, _getLateJoinSpawnPoint()); var entity = _entityManager.ForceSpawnEntityAt(PlayerPrototypeName, _getLateJoinSpawnPoint());
@@ -643,4 +654,38 @@ namespace Content.Server.GameTicking
ticker.ToggleReady(player, bool.Parse(args[0])); 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<IGameTicker>();
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);
}
}
} }

View File

@@ -33,5 +33,7 @@ namespace Content.Server.Interfaces.GameTicking
T AddGameRule<T>() where T : GameRule, new(); T AddGameRule<T>() where T : GameRule, new();
void RemoveGameRule(GameRule rule); void RemoveGameRule(GameRule rule);
IEnumerable<GameRule> ActiveGameRules { get; } IEnumerable<GameRule> ActiveGameRules { get; }
void SetStartPreset(Type type);
} }
} }

View File

@@ -45,4 +45,9 @@
- delete - delete
- tp - tp
- tpgrid - tpgrid
- setgamepreset
- startround
- endround
- restartround
- respawn
CanViewVar: true CanViewVar: true

View File

@@ -45,13 +45,13 @@ binds:
key: MouseMiddle key: MouseMiddle
type: State type: State
- function: SwapHands - function: SwapHands
key: Tab key: X
type: State type: State
- function: Drop - function: Drop
key: Q key: Q
type: State type: State
- function: ActivateItemInHand - function: ActivateItemInHand
key: F key: Z
type: State type: State
- function: OpenCharacterMenu - function: OpenCharacterMenu
key: C key: C