Patched Actions Rework (#6899)
* Rejig Actions * fix merge errors * lambda-b-gon * fix PAI, add innate actions * Revert "fix PAI, add innate actions" This reverts commit 4b501ac083e979e31ebd98d7b98077e0dbdd344b. * Just fix by making nullable. if only require: true actually did something somehow. * Make AddActions() ensure an actions component and misc comments * misc cleanup * Limit range even when not checking for obstructions * remove old guardian code * rename function and make EntityUid nullable * fix magboot bug * fix action search menu * make targeting toggle all equivalent actions * fix combat popups (enabling <-> disabling) * fix networking * Allow action locking * prevent telepathy
This commit is contained in:
@@ -1,30 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Act
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements behavior when an entity is disarmed.
|
||||
/// </summary>
|
||||
[RequiresExplicitImplementation, Obsolete("Use the directed event instead.")]
|
||||
public interface IDisarmedAct
|
||||
{
|
||||
/// <summary>
|
||||
/// Behavior when the entity is disarmed.
|
||||
/// Return true to prevent the default disarm behavior,
|
||||
/// or rest of IDisarmedAct behaviors that come after this one from happening.
|
||||
/// </summary>
|
||||
bool Disarmed(DisarmedActEvent @event);
|
||||
|
||||
/// <summary>
|
||||
/// Priority for this disarm act.
|
||||
/// Used to determine act execution order.
|
||||
/// </summary>
|
||||
int Priority => 0;
|
||||
}
|
||||
|
||||
public sealed class DisarmedActEvent : HandledEntityEventArgs
|
||||
public sealed class DisarmedEvent : HandledEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity being disarmed.
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using Content.Server.CombatMode;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Popups;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class CombatMode : IToggleAction
|
||||
{
|
||||
public bool DoToggleAction(ToggleActionEventArgs args)
|
||||
{
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(args.Performer, out CombatModeComponent? combatMode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
args.Performer.PopupMessage(Loc.GetString(args.ToggledOn ? "hud-combat-enabled" : "hud-combat-disabled"));
|
||||
combatMode.IsInCombatMode = args.ToggledOn;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Actions.Behaviors.Item;
|
||||
using Content.Shared.Cooldown;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Just shows a popup message.asd
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class DebugInstant : IInstantAction, IInstantItemAction
|
||||
{
|
||||
[DataField("message")] public string Message { get; [UsedImplicitly] private set; } = "Instant action used.";
|
||||
[DataField("cooldown")] public float Cooldown { get; [UsedImplicitly] private set; }
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class DebugTargetEntity : ITargetEntityAction, ITargetEntityItemAction
|
||||
{
|
||||
public void DoTargetEntityAction(TargetEntityItemActionEventArgs args)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
args.Performer.PopupMessageEveryone(entMan.GetComponent<MetaDataComponent>(args.Item).EntityName + ": Clicked " +
|
||||
entMan.GetComponent<MetaDataComponent>(args.Target).EntityName);
|
||||
}
|
||||
|
||||
public void DoTargetEntityAction(TargetEntityActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone("Clicked " + IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(args.Target).EntityName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class DebugTargetPoint : ITargetPointAction, ITargetPointItemAction
|
||||
{
|
||||
public void DoTargetPointAction(TargetPointItemActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(args.Item).EntityName + ": Clicked local position " +
|
||||
args.Target);
|
||||
}
|
||||
|
||||
public void DoTargetPointAction(TargetPointActionEventArgs args)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone("Clicked local position " +
|
||||
args.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Actions.Behaviors.Item;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class DebugToggle : IToggleAction, IToggleItemAction
|
||||
{
|
||||
[DataField("messageOn")] public string MessageOn { get; private set; } = "on!";
|
||||
[DataField("messageOff")] public string MessageOff { get; private set; } = "off!";
|
||||
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (args.ToggledOn)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(entMan.GetComponent<MetaDataComponent>(args.Item).EntityName + ": " + MessageOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(entMan.GetComponent<MetaDataComponent>(args.Item).EntityName + ": " +MessageOff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DoToggleAction(ToggleActionEventArgs args)
|
||||
{
|
||||
if (args.ToggledOn)
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(MessageOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Performer.PopupMessageEveryone(MessageOff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.Act;
|
||||
using Content.Server.Actions.Events;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Sound;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class DisarmAction : ITargetEntityAction
|
||||
{
|
||||
[DataField("failProb")] private float _failProb = 0.4f;
|
||||
[DataField("pushProb")] private float _pushProb = 0.4f;
|
||||
[DataField("cooldown")] private float _cooldown = 1.5f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("punchMissSound")]
|
||||
private SoundSpecifier PunchMissSound { get; } = new SoundPathSpecifier("/Audio/Weapons/punchmiss.ogg");
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("disarmSuccessSound")]
|
||||
private SoundSpecifier DisarmSuccessSound { get; } = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg");
|
||||
public void DoTargetEntityAction(TargetEntityActionEventArgs args)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var disarmedActs = entMan.GetComponents<IDisarmedAct>(args.Target).ToArray();
|
||||
var attemptEvent = new DisarmAttemptEvent(args.Target, args.Performer);
|
||||
|
||||
entMan.EventBus.RaiseLocalEvent(args.Target, attemptEvent);
|
||||
|
||||
if (attemptEvent.Cancelled)
|
||||
return;
|
||||
|
||||
var sys = EntitySystem.Get<InteractionSystem>();
|
||||
|
||||
if (!sys.InRangeUnobstructed(args.Performer, args.Target)) return;
|
||||
|
||||
if (disarmedActs.Length == 0)
|
||||
{
|
||||
if (entMan.TryGetComponent(args.Performer, out ActorComponent? actor))
|
||||
{
|
||||
// Fall back to a normal interaction with the entity
|
||||
var player = actor.PlayerSession;
|
||||
var coordinates = entMan.GetComponent<TransformComponent>(args.Target).Coordinates;
|
||||
var target = args.Target;
|
||||
sys.HandleUseInteraction(player, coordinates, target);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entMan.TryGetComponent<SharedActionsComponent?>(args.Performer, out var actions)) return;
|
||||
if (args.Target == args.Performer || !EntitySystem.Get<ActionBlockerSystem>().CanAttack(args.Performer)) return;
|
||||
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
var system = EntitySystem.Get<MeleeWeaponSystem>();
|
||||
|
||||
var diff = entMan.GetComponent<TransformComponent>(args.Target).MapPosition.Position - entMan.GetComponent<TransformComponent>(args.Performer).MapPosition.Position;
|
||||
var angle = Angle.FromWorldVec(diff);
|
||||
|
||||
actions.Cooldown(ActionType.Disarm, Cooldowns.SecondsFromNow(_cooldown));
|
||||
|
||||
if (random.Prob(_failProb))
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(args.Performer), PunchMissSound.GetSound(), args.Performer, AudioHelpers.WithVariation(0.025f));
|
||||
|
||||
args.Performer.PopupMessageOtherClients(Loc.GetString("disarm-action-popup-message-other-clients",
|
||||
("performerName", entMan.GetComponent<MetaDataComponent>(args.Performer).EntityName),
|
||||
("targetName", entMan.GetComponent<MetaDataComponent>(args.Target).EntityName)));
|
||||
args.Performer.PopupMessageCursor(Loc.GetString("disarm-action-popup-message-cursor",
|
||||
("targetName", entMan.GetComponent<MetaDataComponent>(args.Target).EntityName)));
|
||||
system.SendLunge(angle, args.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
system.SendAnimation("disarm", angle, args.Performer, args.Performer, new[] { args.Target });
|
||||
|
||||
var eventArgs = new DisarmedActEvent() { Target = args.Target, Source = args.Performer, PushProbability = _pushProb };
|
||||
|
||||
entMan.EventBus.RaiseLocalEvent(args.Target, eventArgs);
|
||||
|
||||
EntitySystem.Get<AdminLogSystem>().Add(LogType.DisarmedAction, LogImpact.Low, $"{entMan.ToPrettyString(args.Performer):user} used disarm on {entMan.ToPrettyString(args.Target):target}");
|
||||
|
||||
// Check if the event has been handled, and if so, do nothing else!
|
||||
if (eventArgs.Handled)
|
||||
return;
|
||||
|
||||
// Sort by priority.
|
||||
Array.Sort(disarmedActs, (a, b) => a.Priority.CompareTo(b.Priority));
|
||||
|
||||
// TODO: Remove this shit.
|
||||
foreach (var disarmedAct in disarmedActs)
|
||||
{
|
||||
if (disarmedAct.Disarmed(eventArgs))
|
||||
return;
|
||||
}
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(args.Performer), DisarmSuccessSound.GetSound(), entMan.GetComponent<TransformComponent>(args.Performer).Coordinates, AudioHelpers.WithVariation(0.025f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Cooldown;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Blink lights and scare livings
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class GhostBoo : IInstantAction
|
||||
{
|
||||
[DataField("radius")] private float _radius = 3;
|
||||
[DataField("cooldown")] private float _cooldown = 120;
|
||||
[DataField("maxTargets")] private int _maxTargets = 3;
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entMan.TryGetComponent<SharedActionsComponent?>(args.Performer, out var actions)) return;
|
||||
|
||||
// find all IGhostBooAffected nearby and do boo on them
|
||||
var ents = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(args.Performer, _radius);
|
||||
|
||||
var booCounter = 0;
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
var ghostBoo = new GhostBooEvent();
|
||||
entMan.EventBus.RaiseLocalEvent(ent, ghostBoo);
|
||||
|
||||
if (ghostBoo.Handled)
|
||||
booCounter++;
|
||||
|
||||
if (booCounter >= _maxTargets)
|
||||
break;
|
||||
}
|
||||
|
||||
actions.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(_cooldown));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using Content.Server.Guardian;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Popups;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Manifests the guardian saved in the action, using the system
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class ToggleGuardianAction : IInstantAction
|
||||
{
|
||||
[DataField("cooldown")] public float Cooldown { get; [UsedImplicitly] private set; }
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (entManager.TryGetComponent(args.Performer, out GuardianHostComponent? hostComponent) &&
|
||||
hostComponent.HostedGuardian != null)
|
||||
{
|
||||
EntitySystem.Get<GuardianSystem>().ToggleGuardian(hostComponent);
|
||||
args.PerformerActions?.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(Cooldown));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Performer.PopupMessage(Loc.GetString("guardian-missing-invalid-action"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Cooldown;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.Instruments;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Pull up MIDI instrument interface for PAIs to "play themselves"
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class PAIMidi : IInstantAction
|
||||
{
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entMan.TryGetComponent<ServerUserInterfaceComponent?>(args.Performer, out var serverUi)) return;
|
||||
if (!entMan.TryGetComponent<ActorComponent?>(args.Performer, out var actor)) return;
|
||||
if (!serverUi.TryGetBoundUserInterface(InstrumentUiKey.Key,out var bui)) return;
|
||||
|
||||
bui.Toggle(actor.PlayerSession);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.CharacterAppearance;
|
||||
using Content.Shared.CharacterAppearance.Components;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Sound;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class ScreamAction : IInstantAction, ISerializationHooks
|
||||
{
|
||||
private const float Variation = 0.125f;
|
||||
private const float Volume = 4f;
|
||||
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
[DataField("male", required: true)] private SoundSpecifier _male = default!;
|
||||
[DataField("female", required: true)] private SoundSpecifier _female = default!;
|
||||
[DataField("wilhelm", required: true)] private SoundSpecifier _wilhelm = default!;
|
||||
|
||||
/// seconds
|
||||
[DataField("cooldown")] private float _cooldown = 10;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanSpeak(args.Performer)) return;
|
||||
if (!_entMan.TryGetComponent<HumanoidAppearanceComponent?>(args.Performer, out var humanoid)) return;
|
||||
if (!_entMan.TryGetComponent<SharedActionsComponent?>(args.Performer, out var actions)) return;
|
||||
|
||||
if (_random.Prob(.01f))
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(args.Performer), _wilhelm.GetSound(), args.Performer, AudioParams.Default.WithVolume(Volume));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (humanoid.Sex)
|
||||
{
|
||||
case Sex.Male:
|
||||
SoundSystem.Play(Filter.Pvs(args.Performer), _male.GetSound(), args.Performer, AudioHelpers.WithVariation(Variation).WithVolume(Volume));
|
||||
break;
|
||||
case Sex.Female:
|
||||
SoundSystem.Play(Filter.Pvs(args.Performer), _female.GetSound(), args.Performer, AudioHelpers.WithVariation(Variation).WithVolume(Volume));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
actions.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(_cooldown));
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Content.Server/Actions/ActionsSystem.cs
Normal file
40
Content.Server/Actions/ActionsSystem.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class ActionsSystem : SharedActionsSystem
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActionsComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(EntityUid uid, ActionsComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
// need to send state to new player.
|
||||
component.Dirty();
|
||||
}
|
||||
|
||||
protected override bool PerformBasicActions(EntityUid user, ActionType action)
|
||||
{
|
||||
var result = base.PerformBasicActions(user, action);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(action.Speech))
|
||||
{
|
||||
_chatMan.EntitySay(user, Loc.GetString(action.Speech));
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Commands;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Actions.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class CooldownAction : IConsoleCommand
|
||||
{
|
||||
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, string argStr, string[] args)
|
||||
{
|
||||
var player = shell.Player as IPlayerSession;
|
||||
if (player?.AttachedEntity is not {} attachedEntity) return;
|
||||
if (args.Length > 2)
|
||||
{
|
||||
var target = args[2];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(attachedEntity, out ServerActionsComponent? actionsComponent))
|
||||
{
|
||||
shell.WriteError("user has no actions component");
|
||||
return;
|
||||
}
|
||||
|
||||
var actionTypeRaw = args[0];
|
||||
if (!Enum.TryParse<ActionType>(actionTypeRaw, out var actionType))
|
||||
{
|
||||
shell.WriteLine("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.WriteLine("unrecognized actionType " + actionType);
|
||||
return;
|
||||
}
|
||||
|
||||
var cooldownStart = IoCManager.Resolve<IGameTiming>().CurTime;
|
||||
if (!uint.TryParse(args[1], out var seconds))
|
||||
{
|
||||
shell.WriteLine("cannot parse seconds: " + args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
var cooldownEnd = cooldownStart.Add(TimeSpan.FromSeconds(seconds));
|
||||
|
||||
actionsComponent.Cooldown(action.ActionType, (cooldownStart, cooldownEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Commands;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Actions.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class GrantAction : IConsoleCommand
|
||||
{
|
||||
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, string argStr, string[] args)
|
||||
{
|
||||
var player = shell.Player as IPlayerSession;
|
||||
if (player?.AttachedEntity == null) return;
|
||||
var attachedEntity = player.AttachedEntity.Value;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
var target = args[1];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (attachedEntity == default) return;
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(attachedEntity, out ServerActionsComponent? actionsComponent))
|
||||
{
|
||||
shell.WriteLine("user has no actions component");
|
||||
return;
|
||||
}
|
||||
|
||||
var actionTypeRaw = args[0];
|
||||
if (!Enum.TryParse<ActionType>(actionTypeRaw, out var actionType))
|
||||
{
|
||||
shell.WriteLine("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.WriteLine("unrecognized actionType " + actionType);
|
||||
return;
|
||||
}
|
||||
actionsComponent.Grant(action.ActionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Commands;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Actions.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class RevokeAction : IConsoleCommand
|
||||
{
|
||||
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, string argStr, string[] args)
|
||||
{
|
||||
var player = shell.Player as IPlayerSession;
|
||||
if (player?.AttachedEntity == null) return;
|
||||
var attachedEntity = player.AttachedEntity.Value;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
var target = args[1];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
if (attachedEntity == default) return;
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(attachedEntity, out ServerActionsComponent? actionsComponent))
|
||||
{
|
||||
shell.WriteLine("user has no actions component");
|
||||
return;
|
||||
}
|
||||
|
||||
var actionTypeRaw = args[0];
|
||||
if (!Enum.TryParse<ActionType>(actionTypeRaw, out var actionType))
|
||||
{
|
||||
shell.WriteLine("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.WriteLine("unrecognized actionType " + actionType);
|
||||
return;
|
||||
}
|
||||
|
||||
actionsComponent.Revoke(action.ActionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Actions.Prototypes;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Server.Actions
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedActionsComponent))]
|
||||
public sealed class ServerActionsComponent : SharedActionsComponent
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
|
||||
private float MaxUpdateRange;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_configManager.OnValueChanged(CVars.NetMaxUpdateRange, OnRangeChanged, true);
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, OnRangeChanged);
|
||||
}
|
||||
|
||||
private void OnRangeChanged(float obj)
|
||||
{
|
||||
MaxUpdateRange = obj;
|
||||
}
|
||||
|
||||
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
||||
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));
|
||||
}
|
||||
|
||||
if (session.AttachedEntity is not {Valid: true} player || 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", _entities.GetComponent<MetaDataComponent>(player).EntityName,
|
||||
attempt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionState.IsOnCooldown(GameTiming))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to use" +
|
||||
" action {1} which is on cooldown", _entities.GetComponent<MetaDataComponent>(player).EntityName,
|
||||
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}", _entities.GetComponent<MetaDataComponent>(player).EntityName,
|
||||
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.EntityExists(targetEntityMsg.Target))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to" +
|
||||
" perform target entity action {1} but could not find entity with " +
|
||||
"provided uid {2}", _entities.GetComponent<MetaDataComponent>(player).EntityName, attempt.Action.Name,
|
||||
targetEntityMsg.Target);
|
||||
return;
|
||||
}
|
||||
if (!CheckRangeAndSetFacing(_entities.GetComponent<TransformComponent>(targetEntityMsg.Target).Coordinates, player)) return;
|
||||
|
||||
attempt.DoTargetEntityAction(player, targetEntityMsg.Target);
|
||||
break;
|
||||
case BehaviorType.None:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private IActionAttempt? ActionAttempt(BasePerformActionMessage message, ICommonSession session)
|
||||
{
|
||||
IActionAttempt? attempt;
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PerformActionMessage performActionMessage:
|
||||
if (!ActionManager.TryGet(performActionMessage.ActionType, out var action))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" unrecognized action {1}", player,
|
||||
performActionMessage.ActionType);
|
||||
return null;
|
||||
}
|
||||
attempt = new ActionAttempt(action);
|
||||
break;
|
||||
case PerformItemActionMessage performItemActionMessage:
|
||||
var type = performItemActionMessage.ActionType;
|
||||
if (!ActionManager.TryGet(type, out var itemAction))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" unrecognized item action {1}",
|
||||
player, type);
|
||||
return null;
|
||||
}
|
||||
|
||||
var item = performItemActionMessage.Item;
|
||||
if (!EntityManager.EntityExists(item))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" item action {1} for unknown item {2}",
|
||||
player, type, item);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!_entities.TryGetComponent<ItemActionsComponent?>(item, out var actionsComponent))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" item action {1} for item {2} which has no ItemActionsComponent",
|
||||
player, type, item);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (actionsComponent.Holder != player)
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to perform" +
|
||||
" item action {1} for item {2} which they are not holding",
|
||||
player, type, 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", player, attempt, message.BehaviorType,
|
||||
attempt.Action.BehaviorType);
|
||||
return null;
|
||||
}
|
||||
|
||||
return attempt;
|
||||
}
|
||||
|
||||
private bool CheckRangeAndSetFacing(EntityCoordinates target, EntityUid player)
|
||||
{
|
||||
// ensure it's within their clickable range
|
||||
var targetWorldPos = target.ToMapPos(EntityManager);
|
||||
var rangeBox = new Box2(_entities.GetComponent<TransformComponent>(player).WorldPosition, _entities.GetComponent<TransformComponent>(player).WorldPosition)
|
||||
.Enlarged(MaxUpdateRange);
|
||||
if (!rangeBox.Contains(targetWorldPos))
|
||||
{
|
||||
Logger.DebugS("action", "user {0} attempted to" +
|
||||
" perform target action further than allowed range",
|
||||
_entities.GetComponent<MetaDataComponent>(player).EntityName);
|
||||
return false;
|
||||
}
|
||||
|
||||
EntitySystem.Get<RotateToFaceSystem>().TryFaceCoordinates(player, targetWorldPos);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Sound;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Actions.Spells
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class GiveItemSpell : IInstantAction
|
||||
{ //TODO: Needs to be an EntityPrototype for proper validation
|
||||
[ViewVariables] [DataField("castMessage")] public string? CastMessage { get; set; } = default!;
|
||||
[ViewVariables] [DataField("cooldown")] public float CoolDown { get; set; } = 1f;
|
||||
[ViewVariables] [DataField("spellItem", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))] public string ItemProto { get; set; } = default!;
|
||||
|
||||
[ViewVariables] [DataField("castSound", required: true)] public SoundSpecifier CastSound { get; set; } = default!;
|
||||
|
||||
//Rubber-band snapping items into player's hands, originally was a workaround, later found it works quite well with stuns
|
||||
//Not sure if needs fixing
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
var caster = args.Performer;
|
||||
|
||||
if (!entMan.TryGetComponent(caster, out HandsComponent? handsComponent))
|
||||
{
|
||||
caster.PopupMessage(Loc.GetString("spell-fail-no-hands"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(caster, null)) return;
|
||||
|
||||
// TODO: Nix when we get EntityPrototype serializers
|
||||
if (!IoCManager.Resolve<IPrototypeManager>().HasIndex<EntityPrototype>(ItemProto))
|
||||
{
|
||||
Logger.Error($"Invalid prototype {ItemProto} supplied for {nameof(GiveItemSpell)}");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Look this is shitty and ideally a test would do it
|
||||
var spawnedProto = entMan.SpawnEntity(ItemProto, entMan.GetComponent<TransformComponent>(caster).MapPosition);
|
||||
|
||||
if (!entMan.TryGetComponent(spawnedProto, out SharedItemComponent? itemComponent))
|
||||
{
|
||||
Logger.Error($"Tried to use {nameof(GiveItemSpell)} but prototype has no {nameof(SharedItemComponent)}?");
|
||||
entMan.DeleteEntity(spawnedProto);
|
||||
return;
|
||||
}
|
||||
|
||||
args.PerformerActions?.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(CoolDown));
|
||||
|
||||
if (CastMessage != null)
|
||||
caster.PopupMessageEveryone(CastMessage);
|
||||
|
||||
handsComponent.PutInHandOrDrop(itemComponent);
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(caster), CastSound.GetSound(), caster);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,21 @@
|
||||
using System;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Behaviors.Item;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Sound;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
@@ -90,6 +81,9 @@ namespace Content.Server.Atmos.Components
|
||||
[DataField("tankFragmentScale")]
|
||||
public float TankFragmentScale { get; set; } = 10 * Atmospherics.OneAtmosphere;
|
||||
|
||||
[DataField("toggleAction", required: true)]
|
||||
public InstantAction ToggleAction = new();
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -165,6 +159,7 @@ namespace Content.Server.Atmos.Components
|
||||
var internals = GetInternalsComponent();
|
||||
if (internals == null) return;
|
||||
IsConnected = internals.TryConnectTank(Owner);
|
||||
EntitySystem.Get<SharedActionsSystem>().SetToggled(ToggleAction, IsConnected);
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
@@ -172,6 +167,7 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
if (!IsConnected) return;
|
||||
IsConnected = false;
|
||||
EntitySystem.Get<SharedActionsSystem>().SetToggled(ToggleAction, false);
|
||||
GetInternalsComponent(owner)?.DisconnectTank();
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -187,9 +183,6 @@ namespace Content.Server.Atmos.Components
|
||||
InternalsConnected = IsConnected,
|
||||
CanConnectInternals = IsFunctional && internals != null
|
||||
});
|
||||
|
||||
if (internals == null || !_entityManager.TryGetComponent<ItemActionsComponent>(Owner, out var itemActions)) return;
|
||||
itemActions.GrantOrUpdate(ItemActionType.ToggleInternals, IsFunctional, IsConnected);
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
@@ -309,22 +302,4 @@ namespace Content.Server.Atmos.Components
|
||||
DisconnectFromInternals(eventArgs.User);
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class ToggleInternalsAction : IToggleItemAction
|
||||
{
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(args.Performer, args.Item))
|
||||
return false;
|
||||
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<GasTankComponent?>(args.Item, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Toggleable;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
@@ -20,6 +19,23 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GasTankComponent, GetVerbsEvent<ActivationVerb>>(AddOpenUIVerb);
|
||||
SubscribeLocalEvent<GasTankComponent, GetActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<GasTankComponent, ToggleActionEvent>(OnActionToggle);
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, GasTankComponent component, GetActionsEvent args)
|
||||
{
|
||||
args.Actions.Add(component.ToggleAction);
|
||||
}
|
||||
|
||||
private void OnActionToggle(EntityUid uid, GasTankComponent component, ToggleActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
component.ToggleInternals();
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void AddOpenUIVerb(EntityUid uid, GasTankComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Actions.Prototypes;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects;
|
||||
|
||||
/// <summary>
|
||||
/// Forces someone to do a certain action, if they have it.
|
||||
/// </summary>
|
||||
public sealed class DoAction : ReagentEffect
|
||||
{
|
||||
[DataField("action", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<ActionPrototype>))]
|
||||
public string Action = default!;
|
||||
|
||||
public override void Effect(ReagentEffectArgs args)
|
||||
{
|
||||
if (args.EntityManager.TryGetComponent(args.SolutionEntity, out SharedActionsComponent? actions))
|
||||
{
|
||||
if (!IoCManager.Resolve<IPrototypeManager>().TryIndex<ActionPrototype>(Action, out var proto))
|
||||
return;
|
||||
|
||||
if (actions.IsGranted(proto.ActionType))
|
||||
{
|
||||
var attempt = new ActionAttempt(proto);
|
||||
attempt.DoInstantAction(args.SolutionEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Content.Server/Chemistry/ReagentEffects/Scream.cs
Normal file
15
Content.Server/Chemistry/ReagentEffects/Scream.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Content.Server.Speech;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects;
|
||||
|
||||
/// <summary>
|
||||
/// Forces someone to scream their lungs out.
|
||||
/// </summary>
|
||||
public sealed class Scream : ReagentEffect
|
||||
{
|
||||
public override void Effect(ReagentEffectArgs args)
|
||||
{
|
||||
EntitySystem.Get<VocalSystem>().TryScream(args.SolutionEntity);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Behaviors.Item;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -38,8 +36,6 @@ namespace Content.Server.Clothing.Components
|
||||
EntitySystem.Get<MagbootsSystem>().UpdateMagbootEffects(container.Owner, Owner, true, this);
|
||||
}
|
||||
|
||||
if(_entMan.TryGetComponent<ItemActionsComponent>(Owner, out var itemActions))
|
||||
itemActions.Toggle(ItemActionType.ToggleMagboots, On);
|
||||
if (_entMan.TryGetComponent<SharedItemComponent>(Owner, out var item))
|
||||
item.EquippedPrefix = On ? "on" : null;
|
||||
if(_entMan.TryGetComponent<SpriteComponent>(Owner, out var sprite))
|
||||
@@ -49,14 +45,9 @@ namespace Content.Server.Clothing.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Toggle(EntityUid user)
|
||||
{
|
||||
On = !On;
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
Toggle(eventArgs.User);
|
||||
On = !On;
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
@@ -64,18 +55,4 @@ namespace Content.Server.Clothing.Components
|
||||
return new MagbootsComponentState(On);
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class ToggleMagbootsAction : IToggleItemAction
|
||||
{
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<MagbootsComponent?>(args.Item, out var magboots))
|
||||
return false;
|
||||
|
||||
magboots.Toggle(args.Performer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using Content.Server.Alert;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Clothing.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Movement.EntitySystems;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Clothing
|
||||
{
|
||||
public sealed class MagbootsSystem : EntitySystem
|
||||
public sealed class MagbootsSystem : SharedMagbootsSystem
|
||||
{
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
|
||||
|
||||
@@ -1,10 +1,82 @@
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Server.Act;
|
||||
using Content.Server.Actions.Events;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Database;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.CombatMode
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class CombatModeSystem : SharedCombatModeSystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly MeleeWeaponSystem _meleeWeaponSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly AdminLogSystem _logSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharedCombatModeComponent, DisarmActionEvent>(OnEntityActionPerform);
|
||||
}
|
||||
|
||||
private void OnEntityActionPerform(EntityUid uid, SharedCombatModeComponent component, DisarmActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!_actionBlockerSystem.CanAttack(args.Performer))
|
||||
return;
|
||||
|
||||
var attemptEvent = new DisarmAttemptEvent(args.Target, args.Performer);
|
||||
RaiseLocalEvent(args.Target, attemptEvent);
|
||||
if (attemptEvent.Cancelled)
|
||||
return;
|
||||
|
||||
var diff = Transform(args.Target).MapPosition.Position - Transform(args.Performer).MapPosition.Position;
|
||||
var angle = Angle.FromWorldVec(diff);
|
||||
|
||||
var filterAll = Filter.Pvs(args.Performer);
|
||||
var filterOther = filterAll.RemoveWhereAttachedEntity(e => e == args.Performer);
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
if (_random.Prob(component.DisarmFailChance))
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(args.Performer), component.DisarmFailSound.GetSound(), args.Performer, AudioHelpers.WithVariation(0.025f));
|
||||
|
||||
var targetName = Name(args.Target);
|
||||
|
||||
var msgOther = Loc.GetString(
|
||||
"disarm-action-popup-message-other-clients",
|
||||
("performerName", Name(args.Performer)),
|
||||
("targetName", targetName));
|
||||
|
||||
var msgUser = Loc.GetString("disarm-action-popup-message-cursor", ("targetName", targetName ));
|
||||
|
||||
_popupSystem.PopupEntity(msgOther, args.Performer, filterOther);
|
||||
_popupSystem.PopupEntity(msgUser, args.Performer, Filter.Entities(args.Performer));
|
||||
|
||||
_meleeWeaponSystem.SendLunge(angle, args.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
_meleeWeaponSystem.SendAnimation("disarm", angle, args.Performer, args.Performer, new[] { args.Target });
|
||||
SoundSystem.Play(filterAll, component.DisarmSuccessSound.GetSound(), args.Performer, AudioHelpers.WithVariation(0.025f));
|
||||
_logSystem.Add(LogType.DisarmedAction, $"{ToPrettyString(args.Performer):user} used disarm on {ToPrettyString(args.Target):target}");
|
||||
|
||||
var eventArgs = new DisarmedEvent() { Target = args.Target, Source = args.Performer, PushProbability = component.DisarmPushChance };
|
||||
RaiseLocalEvent(args.Target, eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,6 @@ namespace Content.Server.Entry
|
||||
else
|
||||
{
|
||||
IoCManager.Resolve<RecipeManager>().Initialize();
|
||||
IoCManager.Resolve<ActionManager>().Initialize();
|
||||
IoCManager.Resolve<BlackboardManager>().Initialize();
|
||||
IoCManager.Resolve<ConsiderationsManager>().Initialize();
|
||||
IoCManager.Resolve<IAdminManager>().Initialize();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Ghost.Components
|
||||
{
|
||||
@@ -9,5 +10,23 @@ namespace Content.Server.Ghost.Components
|
||||
public sealed class GhostComponent : SharedGhostComponent
|
||||
{
|
||||
public TimeSpan TimeOfDeath { get; set; } = TimeSpan.Zero;
|
||||
|
||||
[DataField("booRadius")]
|
||||
public float BooRadius = 3;
|
||||
|
||||
[DataField("booMaxTargets")]
|
||||
public int BooMaxTargets = 3;
|
||||
|
||||
[DataField("action")]
|
||||
public InstantAction Action = new()
|
||||
{
|
||||
UseDelay = TimeSpan.FromSeconds(120),
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("Interface/Actions/scream.png")),
|
||||
Name = "action-name-boo",
|
||||
Description = "action-description-boo",
|
||||
Event = new BooActionEvent(),
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class BooActionEvent : PerformActionEvent { }
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Server.Mind.Components;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Visible;
|
||||
using Content.Server.Warps;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Follower;
|
||||
using Content.Shared.Ghost;
|
||||
@@ -15,10 +16,6 @@ using Content.Shared.Movement.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Ghost
|
||||
@@ -30,7 +27,9 @@ namespace Content.Server.Ghost
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly GameTicker _ticker = default!;
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly FollowerSystem _followerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -51,6 +50,30 @@ namespace Content.Server.Ghost
|
||||
SubscribeNetworkEvent<GhostReturnToBodyRequest>(OnGhostReturnToBodyRequest);
|
||||
SubscribeNetworkEvent<GhostWarpToLocationRequestEvent>(OnGhostWarpToLocationRequest);
|
||||
SubscribeNetworkEvent<GhostWarpToTargetRequestEvent>(OnGhostWarpToTargetRequest);
|
||||
|
||||
SubscribeLocalEvent<GhostComponent, BooActionEvent>(OnActionPerform);
|
||||
}
|
||||
private void OnActionPerform(EntityUid uid, GhostComponent component, BooActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var ents = _lookup.GetEntitiesInRange(args.Performer, component.BooRadius);
|
||||
|
||||
var booCounter = 0;
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
var ghostBoo = new GhostBooEvent();
|
||||
RaiseLocalEvent(ent, ghostBoo);
|
||||
|
||||
if (ghostBoo.Handled)
|
||||
booCounter++;
|
||||
|
||||
if (booCounter >= component.BooMaxTargets)
|
||||
break;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnRelayMoveInput(EntityUid uid, GhostOnMoveComponent component, RelayMoveInputEvent args)
|
||||
@@ -78,6 +101,8 @@ namespace Content.Server.Ghost
|
||||
}
|
||||
|
||||
component.TimeOfDeath = _gameTiming.RealTime;
|
||||
|
||||
_actions.AddAction(uid, component.Action, null);
|
||||
}
|
||||
|
||||
private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args)
|
||||
@@ -98,6 +123,8 @@ namespace Content.Server.Ghost
|
||||
{
|
||||
eye.VisibilityMask &= ~(uint) VisibilityFlags.Ghost;
|
||||
}
|
||||
|
||||
_actions.RemoveAction(uid, component.Action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Guardian
|
||||
{
|
||||
@@ -22,5 +23,17 @@ namespace Content.Server.Guardian
|
||||
/// Container which holds the guardian
|
||||
/// </summary>
|
||||
[ViewVariables] public ContainerSlot GuardianContainer = default!;
|
||||
|
||||
[DataField("action")]
|
||||
public InstantAction Action = new()
|
||||
{
|
||||
Name = "action-name-guardian",
|
||||
Description = "action-description-guardian",
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("Interface/Actions/manifest.png")),
|
||||
UseDelay = TimeSpan.FromSeconds(2),
|
||||
Event = new GuardianToggleActionEvent(),
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class GuardianToggleActionEvent : PerformActionEvent { };
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Examine;
|
||||
@@ -13,10 +11,6 @@ using Content.Shared.MobState;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -30,6 +24,7 @@ namespace Content.Server.Guardian
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -50,9 +45,22 @@ namespace Content.Server.Guardian
|
||||
SubscribeLocalEvent<GuardianHostComponent, MobStateChangedEvent>(OnHostStateChange);
|
||||
SubscribeLocalEvent<GuardianHostComponent, ComponentShutdown>(OnHostShutdown);
|
||||
|
||||
SubscribeLocalEvent<GuardianHostComponent, GuardianToggleActionEvent>(OnPerformAction);
|
||||
|
||||
SubscribeLocalEvent<GuardianComponent, AttackAttemptEvent>(OnGuardianAttackAttempt);
|
||||
}
|
||||
|
||||
private void OnPerformAction(EntityUid uid, GuardianHostComponent component, GuardianToggleActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (component.HostedGuardian != null)
|
||||
ToggleGuardian(component);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGuardianUnplayer(EntityUid uid, GuardianComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
var host = component.Host;
|
||||
@@ -74,12 +82,14 @@ namespace Content.Server.Guardian
|
||||
private void OnHostInit(EntityUid uid, GuardianHostComponent component, ComponentInit args)
|
||||
{
|
||||
component.GuardianContainer = uid.EnsureContainer<ContainerSlot>("GuardianContainer");
|
||||
_actionSystem.AddAction(uid, component.Action, null);
|
||||
}
|
||||
|
||||
private void OnHostShutdown(EntityUid uid, GuardianHostComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (component.HostedGuardian == null) return;
|
||||
EntityManager.QueueDeleteEntity(component.HostedGuardian.Value);
|
||||
_actionSystem.RemoveAction(uid, component.Action);
|
||||
}
|
||||
|
||||
private void OnGuardianAttackAttempt(EntityUid uid, GuardianComponent component, AttackAttemptEvent args)
|
||||
@@ -151,9 +161,6 @@ namespace Content.Server.Guardian
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't work without actions
|
||||
EntityManager.EnsureComponent<ServerActionsComponent>(target);
|
||||
|
||||
if (component.Injecting) return;
|
||||
|
||||
component.Injecting = true;
|
||||
@@ -175,8 +182,7 @@ namespace Content.Server.Guardian
|
||||
comp.Used ||
|
||||
!TryComp<HandsComponent>(ev.User, out var hands) ||
|
||||
!hands.IsHolding(comp.Owner) ||
|
||||
HasComp<GuardianHostComponent>(ev.Target) ||
|
||||
!TryComp<SharedActionsComponent>(ev.Target, out var actions))
|
||||
HasComp<GuardianHostComponent>(ev.Target))
|
||||
{
|
||||
comp.Injecting = false;
|
||||
return;
|
||||
@@ -194,8 +200,6 @@ namespace Content.Server.Guardian
|
||||
{
|
||||
guardianComponent.Host = ev.Target;
|
||||
|
||||
// Grant the user the recall action and notify them
|
||||
actions.Grant(ActionType.ManifestGuardian);
|
||||
SoundSystem.Play(Filter.Entities(ev.Target), "/Audio/Effects/guardian_inject.ogg", ev.Target);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-created"), ev.Target, Filter.Entities(ev.Target));
|
||||
|
||||
@@ -13,27 +13,19 @@ using Content.Shared.Popups;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Hands.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedHandsComponent))]
|
||||
#pragma warning disable 618
|
||||
public sealed class HandsComponent : SharedHandsComponent, IBodyPartAdded, IBodyPartRemoved, IDisarmedAct
|
||||
public sealed class HandsComponent : SharedHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
||||
#pragma warning restore 618
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
|
||||
[DataField("disarmedSound")] SoundSpecifier _disarmedSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg");
|
||||
|
||||
int IDisarmedAct.Priority => int.MaxValue; // We want this to be the last disarm act to run.
|
||||
|
||||
#region Pull/Disarm
|
||||
|
||||
void IBodyPartAdded.BodyPartAdded(BodyPartAddedEventArgs args)
|
||||
@@ -62,33 +54,10 @@ namespace Content.Server.Hands.Components
|
||||
RemoveHand(args.Slot);
|
||||
}
|
||||
|
||||
bool IDisarmedAct.Disarmed(DisarmedActEvent @event)
|
||||
{
|
||||
if (BreakPulls())
|
||||
return false;
|
||||
|
||||
var source = @event.Source;
|
||||
var target = @event.Target;
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(source), _disarmedSound.GetSound(), source, AudioHelpers.WithVariation(0.025f));
|
||||
|
||||
if (ActiveHand != null && Drop(ActiveHand, false))
|
||||
{
|
||||
source.PopupMessageOtherClients(Loc.GetString("hands-component-disarm-success-others-message", ("disarmer", _entities.GetComponent<MetaDataComponent>(source).EntityName), ("disarmed", _entities.GetComponent<MetaDataComponent>(target).EntityName)));
|
||||
source.PopupMessageCursor(Loc.GetString("hands-component-disarm-success-message", ("disarmed", _entities.GetComponent<MetaDataComponent>(target).EntityName)));
|
||||
}
|
||||
else
|
||||
{
|
||||
source.PopupMessageOtherClients(Loc.GetString("hands-component-shove-success-others-message", ("shover", _entities.GetComponent<MetaDataComponent>(source).EntityName), ("shoved", _entities.GetComponent<MetaDataComponent>(target).EntityName)));
|
||||
source.PopupMessageCursor(Loc.GetString("hands-component-shove-success-message", ("shoved", _entities.GetComponent<MetaDataComponent>(target).EntityName)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool BreakPulls()
|
||||
public bool BreakPulls()
|
||||
{
|
||||
// What is this API??
|
||||
// I just wanted to do actions not deal with this shit...
|
||||
if (!_entities.TryGetComponent(Owner, out SharedPullerComponent? puller)
|
||||
|| puller.Pulling is not {Valid: true} pulling || !_entities.TryGetComponent(puller.Pulling.Value, out SharedPullableComponent? pullable))
|
||||
return false;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Act;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Server.Strip;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Server.Throwing;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Database;
|
||||
@@ -18,6 +21,7 @@ using Content.Shared.Popups;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Input.Binding;
|
||||
@@ -38,6 +42,7 @@ namespace Content.Server.Hands.Systems
|
||||
[Dependency] private readonly AdminLogSystem _logSystem = default!;
|
||||
[Dependency] private readonly StrippableSystem _strippableSystem = default!;
|
||||
[Dependency] private readonly SharedHandVirtualItemSystem _virtualSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -48,6 +53,7 @@ namespace Content.Server.Hands.Systems
|
||||
SubscribeNetworkEvent<ClientInteractUsingInHandMsg>(HandleInteractUsingInHand);
|
||||
SubscribeNetworkEvent<UseInHandMsg>(HandleUseInHand);
|
||||
SubscribeNetworkEvent<MoveItemFromHandMsg>(HandleMoveItemFromHand);
|
||||
SubscribeLocalEvent<HandsComponent, DisarmedEvent>(OnDisarmed, before: new[] { typeof(StunSystem) });
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PullAttemptMessage>(HandlePullAttempt);
|
||||
SubscribeLocalEvent<HandsComponent, PullStartedMessage>(HandlePullStarted);
|
||||
@@ -76,6 +82,26 @@ namespace Content.Server.Hands.Systems
|
||||
args.State = new HandsComponentState(hands.Hands, hands.ActiveHand);
|
||||
}
|
||||
|
||||
private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent args)
|
||||
{
|
||||
if (args.Handled || component.BreakPulls())
|
||||
return;
|
||||
|
||||
if (component.ActiveHand == null || !component.Drop(component.ActiveHand, false))
|
||||
return;
|
||||
|
||||
var targetName = Name(args.Target);
|
||||
|
||||
var msgOther = Loc.GetString("hands-component-disarm-success-others-message", ("disarmer", Name(args.Source)), ("disarmed", targetName));
|
||||
var msgUser = Loc.GetString("hands-component-disarm-success-message", ("disarmed", targetName));
|
||||
|
||||
var filter = Filter.Pvs(args.Source).RemoveWhereAttachedEntity(e => e == args.Source);
|
||||
_popupSystem.PopupEntity(msgOther, args.Source, filter);
|
||||
_popupSystem.PopupEntity(msgUser, args.Source, Filter.Entities(args.Source));
|
||||
|
||||
args.Handled = true; // no shove/stun.
|
||||
}
|
||||
|
||||
#region EntityInsertRemove
|
||||
public override void RemoveHeldEntityFromHand(EntityUid uid, Hand hand, SharedHandsComponent? hands = null)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace Content.Server.IoC
|
||||
IoCManager.Register<IServerPreferencesManager, ServerPreferencesManager>();
|
||||
IoCManager.Register<IServerDbManager, ServerDbManager>();
|
||||
IoCManager.Register<RecipeManager, RecipeManager>();
|
||||
IoCManager.Register<ActionManager, ActionManager>();
|
||||
IoCManager.Register<INodeGroupFactory, NodeGroupFactory>();
|
||||
IoCManager.Register<BlackboardManager, BlackboardManager>();
|
||||
IoCManager.Register<ConsiderationsManager, ConsiderationsManager>();
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Threading.Tasks;
|
||||
using Content.Server.Clothing.Components;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions.Behaviors.Item;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Light.Component;
|
||||
@@ -48,17 +47,4 @@ namespace Content.Server.Light.Components
|
||||
/// </summary>
|
||||
public byte? LastLevel;
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed class ToggleLightAction : IToggleItemAction
|
||||
{
|
||||
public bool DoToggleAction(ToggleItemActionEventArgs args)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(args.Performer, args.Item)) return false;
|
||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<HandheldLightComponent?>(args.Item, out var lightComponent)) return false;
|
||||
if (lightComponent.Activated == args.ToggledOn) return false;
|
||||
return EntitySystem.Get<HandheldLightSystem>().ToggleStatus(args.Performer, lightComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Light.Components
|
||||
{
|
||||
@@ -16,5 +14,8 @@ namespace Content.Server.Light.Components
|
||||
public SoundSpecifier ToggleSound = new SoundPathSpecifier("/Audio/Items/flashlight_pda.ogg");
|
||||
|
||||
[ViewVariables] public bool LightOn = false;
|
||||
|
||||
[DataField("toggleAction", required: true)]
|
||||
public InstantAction ToggleAction = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Clothing.Components;
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Light.Component;
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.Toggleable;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -30,6 +24,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly ActionsSystem _actionSystem = default!;
|
||||
|
||||
// TODO: Ideally you'd be able to subscribe to power stuff to get events at certain percentages.. or something?
|
||||
// But for now this will be better anyway.
|
||||
@@ -47,6 +42,27 @@ namespace Content.Server.Light.EntitySystems
|
||||
SubscribeLocalEvent<HandheldLightComponent, GetVerbsEvent<ActivationVerb>>(AddToggleLightVerb);
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, ActivateInWorldEvent>(OnActivate);
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, GetActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<HandheldLightComponent, ToggleActionEvent>(OnToggleAction);
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, HandheldLightComponent component, GetActionsEvent args)
|
||||
{
|
||||
args.Actions.Add(component.ToggleAction);
|
||||
}
|
||||
|
||||
private void OnToggleAction(EntityUid uid, HandheldLightComponent component, ToggleActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (component.Activated)
|
||||
TurnOff(component);
|
||||
else
|
||||
TurnOn(args.Performer, component);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, HandheldLightComponent component, ref ComponentGetState args)
|
||||
@@ -155,7 +171,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
|
||||
SetState(component, false);
|
||||
component.Activated = false;
|
||||
UpdateLightAction(component);
|
||||
_activeLights.Remove(component);
|
||||
component.LastLevel = null;
|
||||
component.Dirty(EntityManager);
|
||||
@@ -174,7 +189,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(component.Owner), component.TurnOnFailSound.GetSound(), component.Owner);
|
||||
_popup.PopupEntity(Loc.GetString("handheld-light-component-cell-missing-message"), component.Owner, Filter.Entities(user));
|
||||
UpdateLightAction(component);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -185,12 +199,10 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(component.Owner), component.TurnOnFailSound.GetSound(), component.Owner);
|
||||
_popup.PopupEntity(Loc.GetString("handheld-light-component-cell-dead-message"), component.Owner, Filter.Entities(user));
|
||||
UpdateLightAction(component);
|
||||
return false;
|
||||
}
|
||||
|
||||
component.Activated = true;
|
||||
UpdateLightAction(component);
|
||||
SetState(component, true);
|
||||
_activeLights.Add(component);
|
||||
component.LastLevel = GetLevel(component);
|
||||
@@ -217,13 +229,8 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
item.EquippedPrefix = on ? "on" : "off";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLightAction(HandheldLightComponent component)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(component.Owner, out ItemActionsComponent? actions)) return;
|
||||
|
||||
actions.Toggle(ItemActionType.ToggleLight, component.Activated);
|
||||
_actionSystem.SetToggled(component.ToggleAction, on);
|
||||
}
|
||||
|
||||
public void TryUpdate(HandheldLightComponent component, float frameTime)
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Light.Events;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Light;
|
||||
using Content.Shared.Toggleable;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using System;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
public sealed class UnpoweredFlashlightSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<UnpoweredFlashlightComponent, GetVerbsEvent<ActivationVerb>>(AddToggleLightVerbs);
|
||||
SubscribeLocalEvent<UnpoweredFlashlightComponent, GetActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<UnpoweredFlashlightComponent, ToggleActionEvent>(OnToggleAction);
|
||||
}
|
||||
|
||||
private void OnToggleAction(EntityUid uid, UnpoweredFlashlightComponent component, ToggleActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
ToggleLight(component);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, UnpoweredFlashlightComponent component, GetActionsEvent args)
|
||||
{
|
||||
args.Actions.Add(component.ToggleAction);
|
||||
}
|
||||
|
||||
private void AddToggleLightVerbs(EntityUid uid, UnpoweredFlashlightComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
@@ -49,7 +66,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
SoundSystem.Play(Filter.Pvs(light.Owner), flashlight.ToggleSound.GetSound(), flashlight.Owner);
|
||||
|
||||
RaiseLocalEvent(flashlight.Owner, new LightToggleEvent(flashlight.LightOn));
|
||||
_actionsSystem.SetToggled(flashlight.ToggleAction, flashlight.LightOn);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Server.PAI
|
||||
{
|
||||
@@ -20,6 +21,7 @@ namespace Content.Server.PAI
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly InstrumentSystem _instrumentSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -30,6 +32,21 @@ namespace Content.Server.PAI
|
||||
SubscribeLocalEvent<PAIComponent, MindAddedMessage>(OnMindAdded);
|
||||
SubscribeLocalEvent<PAIComponent, MindRemovedMessage>(OnMindRemoved);
|
||||
SubscribeLocalEvent<PAIComponent, GetVerbsEvent<ActivationVerb>>(AddWipeVerb);
|
||||
|
||||
SubscribeLocalEvent<PAIComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<PAIComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, PAIComponent component, ComponentStartup args)
|
||||
{
|
||||
if (component.MidiAction != null)
|
||||
_actionsSystem.AddAction(uid, component.MidiAction, null);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, PAIComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (component.MidiAction != null)
|
||||
_actionsSystem.RemoveAction(uid, component.MidiAction);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, PAIComponent component, ExaminedEvent args)
|
||||
|
||||
42
Content.Server/Speech/Components/VocalComponent.cs
Normal file
42
Content.Server/Speech/Components/VocalComponent.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
||||
namespace Content.Server.Speech.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component required for entities to be able to scream.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class VocalComponent : Component
|
||||
{
|
||||
[DataField("maleScream")]
|
||||
public SoundSpecifier MaleScream = new SoundCollectionSpecifier("MaleScreams");
|
||||
|
||||
[DataField("femaleScream")]
|
||||
public SoundSpecifier FemaleScream = new SoundCollectionSpecifier("FemaleScreams");
|
||||
|
||||
[DataField("wilhelm")]
|
||||
public SoundSpecifier Wilhelm = new SoundPathSpecifier("/Audio/Voice/Human/wilhelm_scream.ogg");
|
||||
|
||||
[DataField("audioParams")]
|
||||
public AudioParams AudioParams = AudioParams.Default.WithVolume(4f);
|
||||
|
||||
public const float Variation = 0.125f;
|
||||
|
||||
// Not using the in-build sound support for actions, given that the sound is modified non-prototype specific factors like gender.
|
||||
[DataField("action", required: true)]
|
||||
public InstantAction Action = new()
|
||||
{
|
||||
UseDelay = TimeSpan.FromSeconds(10),
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("Interface/Actions/scream.png")),
|
||||
Name = "action-name-scream",
|
||||
Description = "AAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
Event = new ScreamActionEvent(),
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class ScreamActionEvent : PerformActionEvent { };
|
||||
86
Content.Server/Speech/VocalSystem.cs
Normal file
86
Content.Server/Speech/VocalSystem.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.CharacterAppearance;
|
||||
using Content.Shared.CharacterAppearance.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Speech;
|
||||
|
||||
/// <summary>
|
||||
/// Fer Screamin
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Or I guess other vocalizations, like laughing. If fun is ever legalized on the station.
|
||||
/// </remarks>
|
||||
public sealed class VocalSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VocalComponent, ScreamActionEvent>(OnActionPerform);
|
||||
SubscribeLocalEvent<VocalComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<VocalComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, VocalComponent component, ComponentStartup args)
|
||||
{
|
||||
_actions.AddAction(uid, component.Action, null);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, VocalComponent component, ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(uid, component.Action);
|
||||
}
|
||||
|
||||
private void OnActionPerform(EntityUid uid, VocalComponent component, ScreamActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = TryScream(uid, component);
|
||||
}
|
||||
|
||||
public bool TryScream(EntityUid uid, VocalComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
if (!_blocker.CanSpeak(uid))
|
||||
return false;
|
||||
|
||||
// Currently this requires humanoid appearance & doesn't have any sort of fall-back or gender-neutral scream.
|
||||
if (!TryComp(uid, out HumanoidAppearanceComponent? humanoid))
|
||||
return false;
|
||||
|
||||
if (_random.Prob(.01f))
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(uid), component.Wilhelm.GetSound(), uid, component.AudioParams);
|
||||
return true;
|
||||
}
|
||||
|
||||
var scale = (float) _random.NextGaussian(1, VocalComponent.Variation);
|
||||
var pitchedParams = component.AudioParams.WithPitchScale(scale);
|
||||
|
||||
switch (humanoid.Sex)
|
||||
{
|
||||
case Sex.Male:
|
||||
SoundSystem.Play(Filter.Pvs(uid), component.MaleScream.GetSound(), uid, pitchedParams);
|
||||
break;
|
||||
case Sex.Female:
|
||||
SoundSystem.Play(Filter.Pvs(uid), component.FemaleScream.GetSound(), uid, pitchedParams);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,10 @@ namespace Content.Server.Stunnable
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StatusEffectsComponent, DisarmedActEvent>(OnDisarmed);
|
||||
SubscribeLocalEvent<StatusEffectsComponent, DisarmedEvent>(OnDisarmed);
|
||||
}
|
||||
|
||||
private void OnDisarmed(EntityUid uid, StatusEffectsComponent status, DisarmedActEvent args)
|
||||
private void OnDisarmed(EntityUid uid, StatusEffectsComponent status, DisarmedEvent args)
|
||||
{
|
||||
if (args.Handled || !_random.Prob(args.PushProbability))
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Ghost.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Verbs;
|
||||
@@ -21,12 +23,29 @@ namespace Content.Server.UserInterface
|
||||
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>((uid, aui, _) => CloseAll(uid, aui));
|
||||
SubscribeLocalEvent<ActivatableUIComponent, UnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
|
||||
SubscribeLocalEvent<ActivatableUIComponent, GotUnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
|
||||
// *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it
|
||||
SubscribeLocalEvent<ActivatableUIComponent, EntParentChangedMessage>(OnParentChanged);
|
||||
SubscribeLocalEvent<ActivatableUIComponent, BoundUIClosedEvent>(OnUIClose);
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIComponent, GetVerbsEvent<ActivationVerb>>(AddOpenUiVerb);
|
||||
|
||||
SubscribeLocalEvent<ServerUserInterfaceComponent, OpenUiActionEvent>(OnActionPerform);
|
||||
}
|
||||
|
||||
private void OnActionPerform(EntityUid uid, ServerUserInterfaceComponent component, OpenUiActionEvent args)
|
||||
{
|
||||
if (args.Handled || args.Key == null)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Performer, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
if (!component.TryGetBoundUserInterface(args.Key, out var bui))
|
||||
return;
|
||||
|
||||
bui.Toggle(actor.PlayerSession);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void AddOpenUiVerb(EntityUid uid, ActivatableUIComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
|
||||
23
Content.Server/UserInterface/OpenUiActionEvent.cs
Normal file
23
Content.Server/UserInterface/OpenUiActionEvent.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.UserInterface;
|
||||
|
||||
public sealed class OpenUiActionEvent : PerformActionEvent, ISerializationHooks
|
||||
{
|
||||
[ViewVariables]
|
||||
public Enum? Key { get; set; }
|
||||
|
||||
[DataField("key", readOnly: true, required: true)]
|
||||
private string _keyRaw = default!;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
|
||||
if (reflectionManager.TryParseEnumReference(_keyRaw, out var key))
|
||||
Key = key;
|
||||
else
|
||||
Logger.Error($"Invalid UI key ({_keyRaw}) in open-UI action");
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace Content.Server.Wieldable
|
||||
SubscribeLocalEvent<WieldableComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<WieldableComponent, ItemWieldedEvent>(OnItemWielded);
|
||||
SubscribeLocalEvent<WieldableComponent, ItemUnwieldedEvent>(OnItemUnwielded);
|
||||
SubscribeLocalEvent<WieldableComponent, UnequippedHandEvent>(OnItemLeaveHand);
|
||||
SubscribeLocalEvent<WieldableComponent, GotUnequippedHandEvent>(OnItemLeaveHand);
|
||||
SubscribeLocalEvent<WieldableComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
|
||||
SubscribeLocalEvent<WieldableComponent, GetVerbsEvent<InteractionVerb>>(AddToggleWieldVerb);
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace Content.Server.Wieldable
|
||||
_virtualItemSystem.DeleteInHandsMatching(args.User.Value, uid);
|
||||
}
|
||||
|
||||
private void OnItemLeaveHand(EntityUid uid, WieldableComponent component, UnequippedHandEvent args)
|
||||
private void OnItemLeaveHand(EntityUid uid, WieldableComponent component, GotUnequippedHandEvent args)
|
||||
{
|
||||
if (!component.Wielded || component.Owner != args.Unequipped)
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user