Actions System + UI (#2710)
Co-authored-by: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com>
This commit is contained in:
39
Content.Server/Actions/DebugInstant.cs
Normal file
39
Content.Server/Actions/DebugInstant.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Just shows a popup message.asd
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DebugInstant : IInstantAction, IInstantItemAction
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
public float Cooldown { get; private set; }
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Message, "message", "Instant action used.");
|
||||
serializer.DataField(this, x => x.Cooldown, "cooldown", 0);
|
||||
}
|
||||
|
||||
public void DoInstantAction(InstantItemActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(Message);
|
||||
if (Cooldown > 0)
|
||||
{
|
||||
args.ItemActions.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(Cooldown));
|
||||
}
|
||||
}
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(Message);
|
||||
args.PerformerActions.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(Cooldown));
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Content.Server/Actions/DebugTargetEntity.cs
Normal file
28
Content.Server/Actions/DebugTargetEntity.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Actions;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DebugTargetEntity : ITargetEntityAction, ITargetEntityItemAction
|
||||
{
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
}
|
||||
|
||||
public void DoTargetEntityAction(TargetEntityItemActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(args.Item.Name + ": Clicked " +
|
||||
args.Target.Name);
|
||||
}
|
||||
|
||||
public void DoTargetEntityAction(TargetEntityActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone("Clicked " +
|
||||
args.Target.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Content.Server/Actions/DebugTargetPoint.cs
Normal file
29
Content.Server/Actions/DebugTargetPoint.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Actions;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DebugTargetPoint : ITargetPointAction, ITargetPointItemAction
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
}
|
||||
|
||||
public void DoTargetPointAction(TargetPointItemActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(args.Item.Name + ": Clicked local position " +
|
||||
args.Target);
|
||||
}
|
||||
|
||||
public void DoTargetPointAction(TargetPointActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone("Clicked local position " +
|
||||
args.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Content.Server/Actions/DebugToggle.cs
Normal file
48
Content.Server/Actions/DebugToggle.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Actions;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DebugToggle : IToggleAction, IToggleItemAction
|
||||
{
|
||||
public string MessageOn { get; private set; }
|
||||
public string MessageOff { get; private set; }
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.MessageOn, "messageOn", "on!");
|
||||
serializer.DataField(this, x => x.MessageOff, "messageOff", "off!");
|
||||
}
|
||||
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
if (args.ToggledOn)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(args.Item.Name + ": " + MessageOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(args.Item.Name + ": " +MessageOff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DoToggleAction(ToggleActionEventArgs args)
|
||||
{
|
||||
if (args.ToggledOn)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(MessageOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(MessageOff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Content.Server/Actions/ScreamAction.cs
Normal file
82
Content.Server/Actions/ScreamAction.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ScreamAction : IInstantAction
|
||||
{
|
||||
private const float Variation = 0.125f;
|
||||
private const float Volume = 4f;
|
||||
|
||||
private List<string> _male;
|
||||
private List<string> _female;
|
||||
private string _wilhelm;
|
||||
/// seconds
|
||||
private float _cooldown;
|
||||
|
||||
private IRobustRandom _random;
|
||||
|
||||
public ScreamAction()
|
||||
{
|
||||
_random = IoCManager.Resolve<IRobustRandom>();
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _male, "male", null);
|
||||
serializer.DataField(ref _female, "female", null);
|
||||
serializer.DataField(ref _wilhelm, "wilhelm", null);
|
||||
serializer.DataField(ref _cooldown, "cooldown", 10);
|
||||
}
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanSpeak(args.Performer)) return;
|
||||
if (!args.Performer.TryGetComponent<HumanoidAppearanceComponent>(out var humanoid)) return;
|
||||
if (!args.Performer.TryGetComponent<SharedActionsComponent>(out var actions)) return;
|
||||
|
||||
if (_random.Prob(.01f) && !string.IsNullOrWhiteSpace(_wilhelm))
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(_wilhelm, args.Performer, AudioParams.Default.WithVolume(Volume));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (humanoid.Sex)
|
||||
{
|
||||
case Sex.Male:
|
||||
if (_male == null) break;
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(_random.Pick(_male), args.Performer,
|
||||
AudioHelpers.WithVariation(Variation).WithVolume(Volume));
|
||||
break;
|
||||
case Sex.Female:
|
||||
if (_female == null) break;
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(_random.Pick(_female), args.Performer,
|
||||
AudioHelpers.WithVariation(Variation).WithVolume(Volume));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
actions.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(_cooldown));
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Content.Server/Alert/Click/ResistFire.cs
Normal file
24
Content.Server/Alert/Click/ResistFire.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
/// <summary>
|
||||
/// Resist fire
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ResistFire : IAlertClick
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public void AlertClicked(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out FlammableComponent flammable))
|
||||
{
|
||||
flammable.Resist();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Content.Server/Alert/Click/StopPiloting.cs
Normal file
24
Content.Server/Alert/Click/StopPiloting.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop piloting shuttle
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class StopPiloting : IAlertClick
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public void AlertClicked(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out ShuttleControllerComponent controller))
|
||||
{
|
||||
controller.RemoveController();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Content.Server/Alert/Click/StopPulling.cs
Normal file
27
Content.Server/Alert/Click/StopPulling.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Pulling;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop pulling something
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class StopPulling : IAlertClick
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public void AlertClicked(ClickAlertEventArgs args)
|
||||
{
|
||||
EntitySystem
|
||||
.Get<SharedPullingSystem>()
|
||||
.GetPulled(args.Player)?
|
||||
.GetComponentOrNull<SharedPullableComponent>()?
|
||||
.TryStopPull();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Content.Server/Alert/Click/Unbuckle.cs
Normal file
24
Content.Server/Alert/Click/Unbuckle.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Content.Server.GameObjects.Components.Buckle;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Shared.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
/// <summary>
|
||||
/// Unbuckles if player is currently buckled.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class Unbuckle : IAlertClick
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public void AlertClicked(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out BuckleComponent buckle))
|
||||
{
|
||||
buckle.TryUnbuckle(args.Player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Content.Server/Commands/Actions/CooldownAction.cs
Normal file
65
Content.Server/Commands/Actions/CooldownAction.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Commands.Actions
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class CooldownAction : IClientCommand
|
||||
{
|
||||
public string Command => "coolaction";
|
||||
public string Description => "Sets a cooldown on an action for a player, defaulting to current player";
|
||||
public string Help => "coolaction <actionType> <seconds> <name or userID, omit for current player>";
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
{
|
||||
if (player == null) return;
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
if (args.Length > 2)
|
||||
{
|
||||
var target = args[2];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (attachedEntity == null) return;
|
||||
if (!attachedEntity.TryGetComponent(out ServerActionsComponent? actionsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no actions component");
|
||||
return;
|
||||
}
|
||||
|
||||
var actionTypeRaw = args[0];
|
||||
if (!Enum.TryParse<ActionType>(actionTypeRaw, out var actionType))
|
||||
{
|
||||
shell.SendText(player, "unrecognized ActionType enum value, please" +
|
||||
" ensure you used correct casing: " + actionTypeRaw);
|
||||
return;
|
||||
}
|
||||
var actionMgr = IoCManager.Resolve<ActionManager>();
|
||||
|
||||
if (!actionMgr.TryGet(actionType, out var action))
|
||||
{
|
||||
shell.SendText(player, "unrecognized actionType " + actionType);
|
||||
return;
|
||||
}
|
||||
|
||||
var cooldownStart = IoCManager.Resolve<IGameTiming>().CurTime;
|
||||
if (!uint.TryParse(args[1], out var seconds))
|
||||
{
|
||||
shell.SendText(player, "cannot parse seconds: " + args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
var cooldownEnd = cooldownStart.Add(TimeSpan.FromSeconds(seconds));
|
||||
|
||||
actionsComponent.Cooldown(action.ActionType, (cooldownStart, cooldownEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Content.Server/Commands/Actions/GrantAction.cs
Normal file
52
Content.Server/Commands/Actions/GrantAction.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Commands.Actions
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class GrantAction : IClientCommand
|
||||
{
|
||||
public string Command => "grantaction";
|
||||
public string Description => "Grants an action to a player, defaulting to current player";
|
||||
public string Help => "grantaction <actionType> <name or userID, omit for current player>";
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
{
|
||||
if (player == null) return;
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
var target = args[1];
|
||||
if (!Commands.CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (attachedEntity == null) return;
|
||||
if (!attachedEntity.TryGetComponent(out ServerActionsComponent? actionsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no actions component");
|
||||
return;
|
||||
}
|
||||
|
||||
var actionTypeRaw = args[0];
|
||||
if (!Enum.TryParse<ActionType>(actionTypeRaw, out var actionType))
|
||||
{
|
||||
shell.SendText(player, "unrecognized ActionType enum value, please" +
|
||||
" ensure you used correct casing: " + actionTypeRaw);
|
||||
return;
|
||||
}
|
||||
var actionMgr = IoCManager.Resolve<ActionManager>();
|
||||
if (!actionMgr.TryGet(actionType, out var action))
|
||||
{
|
||||
shell.SendText(player, "unrecognized actionType " + actionType);
|
||||
return;
|
||||
}
|
||||
actionsComponent.Grant(action.ActionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Content.Server/Commands/Actions/RevokeAction.cs
Normal file
53
Content.Server/Commands/Actions/RevokeAction.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Commands.Actions
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class RevokeAction : IClientCommand
|
||||
{
|
||||
public string Command => "revokeaction";
|
||||
public string Description => "Revokes an action from a player, defaulting to current player";
|
||||
public string Help => "revokeaction <actionType> <name or userID, omit for current player>";
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
{
|
||||
if (player == null) return;
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
var target = args[1];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
if (attachedEntity == null) return;
|
||||
if (!attachedEntity.TryGetComponent(out ServerActionsComponent? actionsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no actions component");
|
||||
return;
|
||||
}
|
||||
|
||||
var actionTypeRaw = args[0];
|
||||
if (!Enum.TryParse<ActionType>(actionTypeRaw, out var actionType))
|
||||
{
|
||||
shell.SendText(player, "unrecognized ActionType enum value, please" +
|
||||
" ensure you used correct casing: " + actionTypeRaw);
|
||||
return;
|
||||
}
|
||||
var actionMgr = IoCManager.Resolve<ActionManager>();
|
||||
if (!actionMgr.TryGet(actionType, out var action))
|
||||
{
|
||||
shell.SendText(player, "unrecognized actionType " + actionType);
|
||||
return;
|
||||
}
|
||||
|
||||
actionsComponent.Revoke(action.ActionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,20 @@ namespace Content.Server.Commands.Alerts
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
{
|
||||
var attachedEntity = player?.AttachedEntity;
|
||||
|
||||
if (attachedEntity == null)
|
||||
if (player?.AttachedEntity == null)
|
||||
{
|
||||
shell.SendText(player, "You don't have an entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
|
||||
if (args.Length > 1)
|
||||
{
|
||||
var target = args[1];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (!CommandUtils.ValidateAttachedEntity(shell, player, attachedEntity)) return;
|
||||
|
||||
if (!attachedEntity.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no alerts component");
|
||||
|
||||
@@ -39,9 +39,6 @@ namespace Content.Server.Commands.Alerts
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (!CommandUtils.ValidateAttachedEntity(shell, player, attachedEntity))
|
||||
return;
|
||||
|
||||
if (!attachedEntity.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no alerts component");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -17,7 +19,7 @@ namespace Content.Server.Commands
|
||||
/// sending a failure to the performer if unable to.
|
||||
/// </summary>
|
||||
public static bool TryGetSessionByUsernameOrId(IConsoleShell shell,
|
||||
string usernameOrId, IPlayerSession performer, out IPlayerSession session)
|
||||
string usernameOrId, IPlayerSession performer, [NotNullWhen(true)] out IPlayerSession? session)
|
||||
{
|
||||
var plyMgr = IoCManager.Resolve<IPlayerManager>();
|
||||
if (plyMgr.TryGetSessionByUsername(usernameOrId, out session)) return true;
|
||||
@@ -37,7 +39,7 @@ namespace Content.Server.Commands
|
||||
/// sending a failure to the performer if unable to.
|
||||
/// </summary>
|
||||
public static bool TryGetAttachedEntityByUsernameOrId(IConsoleShell shell,
|
||||
string usernameOrId, IPlayerSession performer, out IEntity attachedEntity)
|
||||
string usernameOrId, IPlayerSession performer, [NotNullWhen(true)] out IEntity? attachedEntity)
|
||||
{
|
||||
attachedEntity = null;
|
||||
if (!TryGetSessionByUsernameOrId(shell, usernameOrId, performer, out var session)) return false;
|
||||
@@ -50,17 +52,5 @@ namespace Content.Server.Commands
|
||||
attachedEntity = session.AttachedEntity;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if attached entity is null, returning false and sending a message
|
||||
/// to performer if not.
|
||||
/// </summary>
|
||||
public static bool ValidateAttachedEntity(IConsoleShell shell, IPlayerSession performer, IEntity attachedEntity)
|
||||
{
|
||||
if (attachedEntity != null) return true;
|
||||
shell.SendText(performer, "User has no attached entity.");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Server.Interfaces.PDA;
|
||||
using Content.Server.Sandbox;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
@@ -81,6 +82,7 @@ namespace Content.Server
|
||||
_gameTicker.Initialize();
|
||||
IoCManager.Resolve<RecipeManager>().Initialize();
|
||||
IoCManager.Resolve<AlertManager>().Initialize();
|
||||
IoCManager.Resolve<ActionManager>().Initialize();
|
||||
IoCManager.Resolve<BlackboardManager>().Initialize();
|
||||
IoCManager.Resolve<ConsiderationsManager>().Initialize();
|
||||
IoCManager.Resolve<IPDAUplinkManager>().Initialize();
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
return;
|
||||
}
|
||||
|
||||
status?.ShowAlert(AlertType.Fire, onClickAlert: OnClickAlert);
|
||||
status?.ShowAlert(AlertType.Fire);
|
||||
|
||||
if (FireStacks > 0)
|
||||
{
|
||||
@@ -152,14 +152,6 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out FlammableComponent flammable))
|
||||
{
|
||||
flammable.Resist();
|
||||
}
|
||||
}
|
||||
|
||||
public void CollideWith(IEntity collidedWith)
|
||||
{
|
||||
if (!collidedWith.TryGetComponent(out FlammableComponent otherFlammable))
|
||||
|
||||
@@ -5,18 +5,22 @@ using Content.Server.Explosions;
|
||||
using Content.Server.GameObjects.Components.Body.Respiratory;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components.Atmos.GasTank;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.ComponentDependencies;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
@@ -30,13 +34,15 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class GasTankComponent : SharedGasTankComponent, IExamine, IGasMixtureHolder, IUse, IDropped, IActivate
|
||||
{
|
||||
private const float MaxExplosionRange = 14f;
|
||||
private const float MaxExplosionRange = 14f;
|
||||
private const float DefaultOutputPressure = Atmospherics.OneAtmosphere;
|
||||
|
||||
private float _pressureResistance;
|
||||
|
||||
private int _integrity = 3;
|
||||
|
||||
[ComponentDependency] private readonly ItemActionsComponent? _itemActions = null;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? _userInterface;
|
||||
|
||||
[ViewVariables] public GasMixture? Air { get; set; }
|
||||
@@ -191,14 +197,18 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
private void UpdateUserInterface(bool initialUpdate = false)
|
||||
{
|
||||
var internals = GetInternalsComponent();
|
||||
_userInterface?.SetState(
|
||||
new GasTankBoundUserInterfaceState
|
||||
{
|
||||
TankPressure = Air?.Pressure ?? 0,
|
||||
OutputPressure = initialUpdate ? OutputPressure : (float?) null,
|
||||
InternalsConnected = IsConnected,
|
||||
CanConnectInternals = IsFunctional && GetInternalsComponent() != null
|
||||
CanConnectInternals = IsFunctional && internals != null
|
||||
});
|
||||
|
||||
if (internals == null) return;
|
||||
_itemActions?.GrantOrUpdate(ItemActionType.ToggleInternals, IsFunctional, IsConnected);
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
@@ -214,8 +224,9 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleInternals()
|
||||
internal void ToggleInternals()
|
||||
{
|
||||
if (!ActionBlockerSystem.CanUse(GetInternalsComponent()?.Owner)) return;
|
||||
if (IsConnected)
|
||||
{
|
||||
DisconnectFromInternals();
|
||||
@@ -311,6 +322,11 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
_integrity++;
|
||||
}
|
||||
|
||||
public void Dropped(DroppedEventArgs eventArgs)
|
||||
{
|
||||
DisconnectFromInternals(eventArgs.User);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open interaction window
|
||||
/// </summary>
|
||||
@@ -341,10 +357,21 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
component.OpenInterface(actor.playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dropped(DroppedEventArgs eventArgs)
|
||||
[UsedImplicitly]
|
||||
public class ToggleInternalsAction : IToggleItemAction
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) {}
|
||||
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
DisconnectFromInternals(eventArgs.User);
|
||||
if (!args.Item.TryGetComponent<GasTankComponent>(out var gasTankComponent)) return false;
|
||||
// no change
|
||||
if (gasTankComponent.IsConnected == args.ToggledOn) return false;
|
||||
gasTankComponent.ToggleInternals();
|
||||
// did we successfully toggle to the desired status?
|
||||
return gasTankComponent.IsConnected == args.ToggledOn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
|
||||
if (Buckled)
|
||||
{
|
||||
_serverAlertsComponent.ShowAlert(BuckledTo != null ? BuckledTo.BuckledAlertType : AlertType.Buckled,
|
||||
onClickAlert: OnClickAlert);
|
||||
_serverAlertsComponent.ShowAlert(BuckledTo?.BuckledAlertType ?? AlertType.Buckled);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -117,14 +116,6 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out BuckleComponent? buckle))
|
||||
{
|
||||
buckle.TryUnbuckle(args.Player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reattaches this entity to the strap, modifying its position and rotation.
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IHandsComponent))]
|
||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||
[ComponentReference(typeof(SharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
@@ -82,7 +83,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHolding(IEntity entity)
|
||||
public override bool IsHolding(IEntity entity)
|
||||
{
|
||||
foreach (var hand in _hands)
|
||||
{
|
||||
@@ -165,6 +166,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
}
|
||||
|
||||
Dirty();
|
||||
|
||||
var success = hand.Container.Insert(item.Owner);
|
||||
if (success)
|
||||
{
|
||||
@@ -172,6 +174,9 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
OnItemChanged?.Invoke();
|
||||
}
|
||||
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().EquippedHandInteraction(Owner, item.Owner,
|
||||
ToSharedHand(hand));
|
||||
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().HandSelectedInteraction(Owner, item.Owner);
|
||||
|
||||
return success;
|
||||
@@ -266,6 +271,9 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
return false;
|
||||
}
|
||||
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedHandInteraction(Owner, item.Owner,
|
||||
ToSharedHand(hand));
|
||||
|
||||
if (doDropInteraction && !DroppedInteraction(item, false))
|
||||
return false;
|
||||
|
||||
@@ -288,6 +296,61 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (slot == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(slot));
|
||||
}
|
||||
|
||||
if (targetContainer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(targetContainer));
|
||||
}
|
||||
|
||||
var hand = GetHand(slot);
|
||||
if (!CanDrop(slot, doMobChecks) || hand?.Entity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hand.Container.CanRemove(hand.Entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetContainer.CanInsert(hand.Entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var item = hand.Entity.GetComponent<ItemComponent>();
|
||||
|
||||
if (!hand.Container.Remove(hand.Entity))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedHandInteraction(Owner, item.Owner,
|
||||
ToSharedHand(hand));
|
||||
|
||||
if (doDropInteraction && !DroppedInteraction(item, doMobChecks))
|
||||
return false;
|
||||
|
||||
item.RemovedFromSlot();
|
||||
|
||||
if (!targetContainer.Insert(item.Owner))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
|
||||
Dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Drop(IEntity entity, EntityCoordinates coords, bool doMobChecks = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (entity == null)
|
||||
@@ -323,57 +386,6 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
return Drop(slot, Owner.Transform.Coordinates, mobChecks, doDropInteraction);
|
||||
}
|
||||
|
||||
public bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (slot == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(slot));
|
||||
}
|
||||
|
||||
if (targetContainer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(targetContainer));
|
||||
}
|
||||
|
||||
var hand = GetHand(slot);
|
||||
if (!CanDrop(slot, doMobChecks) || hand?.Entity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hand.Container.CanRemove(hand.Entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetContainer.CanInsert(hand.Entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var item = hand.Entity.GetComponent<ItemComponent>();
|
||||
|
||||
if (!hand.Container.Remove(hand.Entity))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (doDropInteraction && !DroppedInteraction(item, doMobChecks))
|
||||
return false;
|
||||
|
||||
item.RemovedFromSlot();
|
||||
|
||||
if (!targetContainer.Insert(item.Owner))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
|
||||
Dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Drop(IEntity entity, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true)
|
||||
{
|
||||
if (entity == null)
|
||||
@@ -463,19 +475,28 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
for (var i = 0; i < _hands.Count; i++)
|
||||
{
|
||||
var location = i == 0
|
||||
? HandLocation.Right
|
||||
: i == _hands.Count - 1
|
||||
? HandLocation.Left
|
||||
: HandLocation.Middle;
|
||||
|
||||
var hand = _hands[i].ToShared(i, location);
|
||||
var hand = _hands[i].ToShared(i, IndexToHandLocation(i));
|
||||
hands[i] = hand;
|
||||
}
|
||||
|
||||
return new HandsComponentState(hands, ActiveHand);
|
||||
}
|
||||
|
||||
private HandLocation IndexToHandLocation(int index)
|
||||
{
|
||||
return index == 0
|
||||
? HandLocation.Right
|
||||
: index == _hands.Count - 1
|
||||
? HandLocation.Left
|
||||
: HandLocation.Middle;
|
||||
}
|
||||
|
||||
private SharedHand ToSharedHand(Hand hand)
|
||||
{
|
||||
var index = _hands.IndexOf(hand);
|
||||
return hand.ToShared(index, IndexToHandLocation(index));
|
||||
}
|
||||
|
||||
public void SwapHands()
|
||||
{
|
||||
if (ActiveHand == null)
|
||||
|
||||
@@ -25,6 +25,7 @@ using static Content.Shared.GameObjects.Components.Inventory.SharedInventoryComp
|
||||
namespace Content.Server.GameObjects.Components.GUI
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedInventoryComponent))]
|
||||
public class InventoryComponent : SharedInventoryComponent, IExAct, IEffectBlocker, IPressureProtection
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
@@ -572,5 +573,20 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsEquipped(IEntity item)
|
||||
{
|
||||
if (item == null) return false;
|
||||
foreach (var containerSlot in _slotContainers.Values)
|
||||
{
|
||||
// we don't want a recursive check here
|
||||
if (containerSlot.Contains(item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Clothing;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.ComponentDependencies;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
@@ -45,6 +53,8 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
[ViewVariables(VVAccess.ReadWrite)] public string? TurnOnFailSound;
|
||||
[ViewVariables(VVAccess.ReadWrite)] public string? TurnOffSound;
|
||||
|
||||
[ComponentDependency] private readonly ItemActionsComponent? _itemActions = null;
|
||||
|
||||
/// <summary>
|
||||
/// Client-side ItemStatus level
|
||||
/// </summary>
|
||||
@@ -98,8 +108,9 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
/// Illuminates the light if it is not active, extinguishes it if it is active.
|
||||
/// </summary>
|
||||
/// <returns>True if the light's status was toggled, false otherwise.</returns>
|
||||
private bool ToggleStatus(IEntity user)
|
||||
public bool ToggleStatus(IEntity user)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanUse(user)) return false;
|
||||
return Activated ? TurnOff() : TurnOn(user);
|
||||
}
|
||||
|
||||
@@ -112,6 +123,7 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
|
||||
SetState(false);
|
||||
Activated = false;
|
||||
UpdateLightAction();
|
||||
|
||||
if (makeNoise)
|
||||
{
|
||||
@@ -132,6 +144,7 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
{
|
||||
if (TurnOnFailSound != null) EntitySystem.Get<AudioSystem>().PlayFromEntity(TurnOnFailSound, Owner);
|
||||
Owner.PopupMessage(user, Loc.GetString("Cell missing..."));
|
||||
UpdateLightAction();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,10 +155,12 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
{
|
||||
if (TurnOnFailSound != null) EntitySystem.Get<AudioSystem>().PlayFromEntity(TurnOnFailSound, Owner);
|
||||
Owner.PopupMessage(user, Loc.GetString("Dead cell..."));
|
||||
UpdateLightAction();
|
||||
return false;
|
||||
}
|
||||
|
||||
Activated = true;
|
||||
UpdateLightAction();
|
||||
SetState(true);
|
||||
|
||||
if (TurnOnSound != null) EntitySystem.Get<AudioSystem>().PlayFromEntity(TurnOnSound, Owner);
|
||||
@@ -175,6 +190,11 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLightAction()
|
||||
{
|
||||
_itemActions?.Toggle(ItemActionType.ToggleLight, Activated);
|
||||
}
|
||||
|
||||
public void OnUpdate(float frameTime)
|
||||
{
|
||||
if (Cell == null)
|
||||
@@ -249,4 +269,17 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ToggleLightAction : IToggleItemAction
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) {}
|
||||
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
if (!args.Item.TryGetComponent<HandheldLightComponent>(out var lightComponent)) return false;
|
||||
if (lightComponent.Activated == args.ToggledOn) return false;
|
||||
return lightComponent.ToggleStatus(args.Performer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Pops up a message when equipped / unequipped (including hands).
|
||||
/// For debugging purposes.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class DebugEquipComponent : Component, IEquipped, IEquippedHand, IUnequipped, IUnequippedHand
|
||||
{
|
||||
public override string Name => "DebugEquip";
|
||||
public void Equipped(EquippedEventArgs eventArgs)
|
||||
{
|
||||
eventArgs.User.PopupMessage("equipped " + Owner.Name);
|
||||
}
|
||||
|
||||
public void EquippedHand(EquippedHandEventArgs eventArgs)
|
||||
{
|
||||
eventArgs.User.PopupMessage("equipped hand " + Owner.Name);
|
||||
}
|
||||
|
||||
public void Unequipped(UnequippedEventArgs eventArgs)
|
||||
{
|
||||
eventArgs.User.PopupMessage("unequipped " + Owner.Name);
|
||||
}
|
||||
|
||||
public void UnequippedHand(UnequippedHandEventArgs eventArgs)
|
||||
{
|
||||
eventArgs.User.PopupMessage("unequipped hand" + Owner.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedActionsComponent))]
|
||||
public sealed class ServerActionsComponent : SharedActionsComponent
|
||||
{
|
||||
[Dependency] private readonly IServerEntityManager _entityManager = default!;
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
if (message is not BasePerformActionMessage performActionMessage) return;
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
var player = session.AttachedEntity;
|
||||
if (player != Owner) return;
|
||||
var attempt = ActionAttempt(performActionMessage, session);
|
||||
if (attempt == null) return;
|
||||
|
||||
if (!attempt.TryGetActionState(this, out var actionState) || !actionState.Enabled)
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to use" +
|
||||
" action {1} which is not granted to them", player.Name,
|
||||
attempt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionState.IsOnCooldown(GameTiming))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to use" +
|
||||
" action {1} which is on cooldown", player.Name,
|
||||
attempt);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (performActionMessage.BehaviorType)
|
||||
{
|
||||
case BehaviorType.Instant:
|
||||
attempt.DoInstantAction(player);
|
||||
break;
|
||||
case BehaviorType.Toggle:
|
||||
if (performActionMessage is not IToggleActionMessage toggleMsg) return;
|
||||
if (toggleMsg.ToggleOn == actionState.ToggledOn)
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to" +
|
||||
" toggle action {1} to {2}, but it is already toggled {2}", player.Name,
|
||||
attempt.Action.Name, toggleMsg.ToggleOn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (attempt.DoToggleAction(player, toggleMsg.ToggleOn))
|
||||
{
|
||||
attempt.ToggleAction(this, toggleMsg.ToggleOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if client predicted the toggle will work, need to reset
|
||||
// that prediction
|
||||
Dirty();
|
||||
}
|
||||
break;
|
||||
case BehaviorType.TargetPoint:
|
||||
if (performActionMessage is not ITargetPointActionMessage targetPointMsg) return;
|
||||
if (!CheckRangeAndSetFacing(targetPointMsg.Target, player)) return;
|
||||
attempt.DoTargetPointAction(player, targetPointMsg.Target);
|
||||
break;
|
||||
case BehaviorType.TargetEntity:
|
||||
if (performActionMessage is not ITargetEntityActionMessage targetEntityMsg) return;
|
||||
if (!EntityManager.TryGetEntity(targetEntityMsg.Target, out var entity))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to" +
|
||||
" perform target entity action {1} but could not find entity with " +
|
||||
"provided uid {2}", player.Name, attempt.Action.Name,
|
||||
targetEntityMsg.Target);
|
||||
return;
|
||||
}
|
||||
if (!CheckRangeAndSetFacing(entity.Transform.Coordinates, player)) return;
|
||||
|
||||
attempt.DoTargetEntityAction(player, entity);
|
||||
break;
|
||||
case BehaviorType.None:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private IActionAttempt? ActionAttempt(BasePerformActionMessage message, ICommonSession session)
|
||||
{
|
||||
IActionAttempt? attempt;
|
||||
switch (message)
|
||||
{
|
||||
case PerformActionMessage performActionMessage:
|
||||
if (!ActionManager.TryGet(performActionMessage.ActionType, out var action))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" unrecognized action {1}", session.AttachedEntity,
|
||||
performActionMessage.ActionType);
|
||||
return null;
|
||||
}
|
||||
attempt = new ActionAttempt(action);
|
||||
break;
|
||||
case PerformItemActionMessage performItemActionMessage:
|
||||
if (!ActionManager.TryGet(performItemActionMessage.ActionType, out var itemAction))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" unrecognized item action {1}",
|
||||
session.AttachedEntity, performItemActionMessage.ActionType);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetEntity(performItemActionMessage.Item, out var item))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" item action {1} for unknown item {2}",
|
||||
session.AttachedEntity, performItemActionMessage.ActionType, performItemActionMessage.Item);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!item.TryGetComponent<ItemActionsComponent>(out var actionsComponent))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" item action {1} for item {2} which has no ItemActionsComponent",
|
||||
session.AttachedEntity, performItemActionMessage.ActionType, item);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (actionsComponent.Holder != session.AttachedEntity)
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" item action {1} for item {2} which they are not holding",
|
||||
session.AttachedEntity, performItemActionMessage.ActionType, item);
|
||||
return null;
|
||||
}
|
||||
|
||||
attempt = new ItemActionAttempt(itemAction, item, actionsComponent);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (message.BehaviorType != attempt.Action.BehaviorType)
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to" +
|
||||
" perform action {1} as a {2} behavior, but this action is actually a" +
|
||||
" {3} behavior", session.AttachedEntity, attempt, message.BehaviorType,
|
||||
attempt.Action.BehaviorType);
|
||||
return null;
|
||||
}
|
||||
|
||||
return attempt;
|
||||
}
|
||||
|
||||
private bool CheckRangeAndSetFacing(EntityCoordinates target, IEntity player)
|
||||
{
|
||||
// ensure it's within their clickable range
|
||||
var targetWorldPos = target.ToMapPos(EntityManager);
|
||||
var rangeBox = new Box2(player.Transform.WorldPosition, player.Transform.WorldPosition)
|
||||
.Enlarged(_entityManager.MaxUpdateRange);
|
||||
if (!rangeBox.Contains(targetWorldPos))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to" +
|
||||
" perform target action further than allowed range",
|
||||
player.Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ActionBlockerSystem.CanChangeDirection(player)) return true;
|
||||
|
||||
// don't set facing unless they clicked far enough away
|
||||
var diff = targetWorldPos - player.Transform.WorldPosition;
|
||||
if (diff.LengthSquared > 0.01f)
|
||||
{
|
||||
player.Transform.LocalRotation = new Angle(diff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
@@ -42,11 +43,6 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new AlertsComponentState(CreateAlertStatesArray());
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
@@ -67,14 +63,21 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Implement clicking other status effects in the HUD
|
||||
if (AlertManager.TryDecode(msg.EncodedAlert, out var alert))
|
||||
if (!IsShowingAlert(msg.AlertType))
|
||||
{
|
||||
PerformAlertClickCallback(alert, player);
|
||||
Logger.DebugS("alert", "user {0} attempted to" +
|
||||
" click alert {1} which is not currently showing for them",
|
||||
player.Name, msg.AlertType);
|
||||
break;
|
||||
}
|
||||
|
||||
if (AlertManager.TryGet(msg.AlertType, out var alert))
|
||||
{
|
||||
alert.OnClick.AlertClicked(new ClickAlertEventArgs(player, alert));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.EncodedAlert);
|
||||
Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.AlertType);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -145,15 +145,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
mind.Mind.Visit(Owner);
|
||||
_controller = entity;
|
||||
|
||||
status.ShowAlert(_pilotingAlertType, onClickAlert: OnClickAlert);
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out ShuttleControllerComponent? controller))
|
||||
{
|
||||
controller.RemoveController();
|
||||
}
|
||||
status.ShowAlert(_pilotingAlertType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Pulling;
|
||||
using Content.Server.GameObjects.Components.Timing;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystemMessages;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
@@ -113,11 +115,9 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the Activate behavior of an object
|
||||
/// Activates the IActivate behavior of an object
|
||||
/// Verifies that the user is capable of doing the use interaction first
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="used"></param>
|
||||
public void TryInteractionActivate(IEntity user, IEntity used)
|
||||
{
|
||||
if (user != null && used != null && ActionBlockerSystem.CanUse(user))
|
||||
@@ -504,7 +504,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the Use behavior of an object
|
||||
/// Activates the IUse behaviors of an entity
|
||||
/// Verifies that the user is capable of doing the use interaction first
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
@@ -518,8 +518,8 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// Activates the IUse behaviors of an entity without first checking
|
||||
/// if the user is capable of doing the use interaction.
|
||||
/// </summary>
|
||||
public void UseInteraction(IEntity user, IEntity used)
|
||||
{
|
||||
@@ -679,6 +679,48 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls EquippedHand on all components that implement the IEquippedHand interface
|
||||
/// on an item.
|
||||
/// </summary>
|
||||
public void EquippedHandInteraction(IEntity user, IEntity item, SharedHand hand)
|
||||
{
|
||||
var equippedHandMessage = new EquippedHandMessage(user, item, hand);
|
||||
RaiseLocalEvent(equippedHandMessage);
|
||||
if (equippedHandMessage.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var comps = item.GetAllComponents<IEquippedHand>().ToList();
|
||||
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
comp.EquippedHand(new EquippedHandEventArgs(user, hand));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls UnequippedHand on all components that implement the IUnequippedHand interface
|
||||
/// on an item.
|
||||
/// </summary>
|
||||
public void UnequippedHandInteraction(IEntity user, IEntity item, SharedHand hand)
|
||||
{
|
||||
var unequippedHandMessage = new UnequippedHandMessage(user, item, hand);
|
||||
RaiseLocalEvent(unequippedHandMessage);
|
||||
if (unequippedHandMessage.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var comps = item.GetAllComponents<IUnequippedHand>().ToList();
|
||||
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
comp.UnequippedHand(new UnequippedHandEventArgs(user, hand));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the Dropped behavior of an object
|
||||
/// Verifies that the user is capable of doing the drop interaction first
|
||||
@@ -757,7 +799,6 @@ namespace Content.Server.GameObjects.EntitySystems.Click
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
||||
@@ -20,6 +20,7 @@ using Content.Server.PDA;
|
||||
using Content.Server.Preferences;
|
||||
using Content.Server.Sandbox;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Shared.Alert;
|
||||
@@ -43,6 +44,7 @@ namespace Content.Server
|
||||
IoCManager.Register<IServerDbManager, ServerDbManager>();
|
||||
IoCManager.Register<RecipeManager, RecipeManager>();
|
||||
IoCManager.Register<AlertManager, AlertManager>();
|
||||
IoCManager.Register<ActionManager, ActionManager>();
|
||||
IoCManager.Register<IPDAUplinkManager,PDAUplinkManager>();
|
||||
IoCManager.Register<INodeGroupFactory, NodeGroupFactory>();
|
||||
IoCManager.Register<INodeGroupManager, NodeGroupManager>();
|
||||
|
||||
Reference in New Issue
Block a user