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\NanoStyle.cs" />
<Compile Include="UserInterface\Placeholder.cs" />
<Compile Include="UserInterface\TutorialButton.cs" />
<Compile Include="Utility\ResourceCacheExtensions.cs" />
<Compile Include="GameObjects\Components\Mobs\SpeciesVisualizer2D.cs" />
</ItemGroup>

View File

@@ -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

View File

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

View File

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

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="GameTicking\GamePreset.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\GameRules\RuleDeathMatch.cs" />
<Compile Include="GameTicking\GameTicker.cs" />

View File

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

View File

@@ -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<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
switch (step)

View File

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

View File

@@ -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)

View File

@@ -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>();
interactionSystem.Interaction(Owner, used, slot.ContainedEntity,
GridCoordinates.Nullspace);

View File

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

View File

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

View File

@@ -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)

View File

@@ -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
/// <summary>
/// Called when using one object on another
/// </summary>
/// <param name="user"></param>
/// <param name="attackwith"></param>
/// <returns></returns>
bool AttackBy(AttackByEventArgs eventArgs);
}
@@ -47,8 +44,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Called when a player directly interacts with an empty hand
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
bool AttackHand(AttackHandEventArgs eventArgs);
}
@@ -65,13 +60,11 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Called when we try to interact with an entity out of range
/// </summary>
/// <param name="user"></param>
/// <param name="attackwith"></param>
/// <param name="clicklocation"></param>
/// <returns></returns>
bool RangedAttackBy(RangedAttackByEventArgs eventArgs);
}
[PublicAPI]
public class RangedAttackByEventArgs : EventArgs
{
public IEntity User { get; set; }
@@ -88,9 +81,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior
/// </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);
}
@@ -109,7 +99,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Called when we activate an object we are holding to use it
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
bool UseEntity(UseEntityEventArgs eventArgs);
}
@@ -127,7 +116,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Called when this component is activated by another entity.
/// </summary>
/// <param name="user">Entity that activated this component.</param>
void Activate(ActivateEventArgs eventArgs);
}
@@ -139,50 +127,66 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Governs interactions during clicking on entities
/// </summary>
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<InputSystem>();
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<ITransformComponent>(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<IHandsComponent>(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<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;
}
// 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<ITransformComponent>().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
}
/// <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>
/// <param name="user"></param>
/// <param name="weapon"></param>
/// <param name="clicklocation"></param>
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<IAfterAttack> afterattacks = weapon.GetAllComponents<IAfterAttack>().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<IAfterAttack>().ToList();
var afterAttackEventArgs = new AfterAttackEventArgs {User = user, ClickLocation = clickLocation};
foreach (var afterAttack in afterAttacks)
{
afterAttack.AfterAttack(afterAttackEventArgs);
}
}
/// <summary>
/// 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>
/// <param name="user"></param>
/// <param name="weapon"></param>
/// <param name="attacked"></param>
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<IAttackBy> interactables = attacked.GetAllComponents<IAttackBy>().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<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;
}
}
//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<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>
/// 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>
/// <param name="user"></param>
/// <param name="attacked"></param>
public void Interaction(IEntity user, IEntity attacked)
{
var message = new AttackHandMessage(user, attacked);
RaiseEvent(message);
if(message.Handled)
return;
List<IAttackHand> interactables = attacked.GetAllComponents<IAttackHand>().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<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;
}
}
//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>
@@ -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
/// </summary>
/// <param name="user"></param>
/// <param name="attacked"></param>
public void UseInteraction(IEntity user, IEntity used)
{
var useMsg = new UseInHandMessage(user, used);
RaiseEvent(useMsg);
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 (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<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;
}
}
@@ -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
/// </summary>
/// <param name="user"></param>
/// <param name="weapon"></param>
/// <param name="attacked"></param>
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<IRangedAttackBy> rangedusables = attacked.GetAllComponents<IRangedAttackBy>().ToList();
//See if we have a ranged attack interaction
for (var i = 0; i < rangedusables.Count; i++)
var rangedAttackBys = attacked.GetAllComponents<IRangedAttackBy>().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<IAfterAttack>().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<IAfterAttack> afterattacks = weapon.GetAllComponents<IAfterAttack>().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
/// <summary>
/// Raised when being clicked on or "attacked" by a user with an object in their hand
/// </summary>
[PublicAPI]
public class AttackByMessage : EntitySystemMessage
{
/// <summary>
@@ -471,7 +501,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// Entity that the User attacked with.
/// </summary>
public IEntity ItemInHand { get; }
/// <summary>
/// Entity that was attacked.
/// </summary>
@@ -494,6 +524,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Raised when being clicked on or "attacked" by a user with an empty hand.
/// </summary>
[PublicAPI]
public class AttackHandMessage : EntitySystemMessage
{
/// <summary>
@@ -521,6 +552,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Raised when being clicked by objects outside the range of direct use.
/// </summary>
[PublicAPI]
public class RangedAttackMessage : EntitySystemMessage
{
/// <summary>
@@ -560,6 +592,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Raised when clicking on another object and no attack event was handled.
/// </summary>
[PublicAPI]
public class AfterAttackMessage : EntitySystemMessage
{
/// <summary>
@@ -599,6 +632,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Raised when using the entity in your hands.
/// </summary>
[PublicAPI]
public class UseInHandMessage : EntitySystemMessage
{
/// <summary>
@@ -626,6 +660,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// <summary>
/// Raised when an entity is activated in the world.
/// </summary>
[PublicAPI]
public class ActivateInWorldMessage : EntitySystemMessage
{
/// <summary>

View File

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

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.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<GameRule> _gameRules = new List<GameRule>();
[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<PresetTraitor>();
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<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()
{
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<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();
void RemoveGameRule(GameRule rule);
IEnumerable<GameRule> ActiveGameRules { get; }
void SetStartPreset(Type type);
}
}

View File

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

View File

@@ -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