UseDelay + ItemCooldown merge (#22502)

This commit is contained in:
AJCM-git
2024-01-03 21:33:09 -04:00
committed by GitHub
parent 42ec9b2967
commit 9c522c8b19
80 changed files with 324 additions and 589 deletions

View File

@@ -1,9 +1,13 @@
using Content.Shared.Clothing.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Tag;
using Content.Shared.Timing;
using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.EntitySystems;
@@ -13,6 +17,8 @@ public abstract class ClothingSystem : EntitySystem
[Dependency] private readonly SharedItemSystem _itemSys = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly InventorySystem _invSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[ValidatePrototypeId<TagPrototype>]
private const string HairTag = "HidesHair";
@@ -21,12 +27,60 @@ public abstract class ClothingSystem : EntitySystem
{
base.Initialize();
SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ClothingComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<ClothingComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<ClothingComponent, ItemMaskToggledEvent>(OnMaskToggled);
}
private void OnUseInHand(Entity<ClothingComponent> ent, ref UseInHandEvent args)
{
if (args.Handled || !ent.Comp.QuickEquip)
return;
var user = args.User;
if (!TryComp(user, out InventoryComponent? inv) ||
!TryComp(user, out HandsComponent? hands))
return;
QuickEquip(ent, (user, inv, hands));
args.Handled = true;
args.ApplyDelay = false;
}
private void QuickEquip(
Entity<ClothingComponent> toEquipEnt,
Entity<InventoryComponent, HandsComponent> userEnt)
{
foreach (var slotDef in userEnt.Comp1.Slots)
{
if (!_invSystem.CanEquip(userEnt, toEquipEnt, slotDef.Name, out _, slotDef, userEnt, toEquipEnt))
continue;
if (_invSystem.TryGetSlotEntity(userEnt, slotDef.Name, out var slotEntity, userEnt))
{
// Item in slot has to be quick equipable as well
if (TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
continue;
if (!_invSystem.TryUnequip(userEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt))
continue;
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt))
continue;
_handsSystem.PickupOrDrop(userEnt, slotEntity.Value, handsComp: userEnt);
}
else
{
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt))
continue;
}
break;
}
}
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
{
@@ -71,7 +125,7 @@ public abstract class ClothingSystem : EntitySystem
clothing.EquippedPrefix = prefix;
_itemSys.VisualsChanged(uid);
Dirty(clothing);
Dirty(uid, clothing);
}
public void SetSlots(EntityUid uid, SlotFlags slots, ClothingComponent? clothing = null)
@@ -80,7 +134,7 @@ public abstract class ClothingSystem : EntitySystem
return;
clothing.Slots = slots;
Dirty(clothing);
Dirty(uid, clothing);
}
/// <summary>
@@ -97,7 +151,7 @@ public abstract class ClothingSystem : EntitySystem
clothing.FemaleMask = otherClothing.FemaleMask;
_itemSys.VisualsChanged(uid);
Dirty(clothing);
Dirty(uid, clothing);
}
#endregion

View File

@@ -1,26 +0,0 @@
using Robust.Shared.Timing;
namespace Content.Shared.Cooldown
{
/// <summary>
/// Utilities for working with cooldowns.
/// </summary>
public static class Cooldowns
{
/// <param name="gameTiming">game timing to use, otherwise will resolve using IoCManager.</param>
/// <returns>a cooldown interval starting at GameTiming.Curtime and ending at (offset) from CurTime.
/// For example, passing TimeSpan.FromSeconds(5) will create an interval
/// from now to 5 seconds from now.</returns>
public static (TimeSpan start, TimeSpan end) FromNow(TimeSpan offset, IGameTiming? gameTiming = null)
{
var now = (gameTiming ?? IoCManager.Resolve<IGameTiming>()).CurTime;
return (now, now + offset);
}
/// <see cref="FromNow"/>
public static (TimeSpan start, TimeSpan end) SecondsFromNow(double seconds, IGameTiming? gameTiming = null)
{
return FromNow(TimeSpan.FromSeconds(seconds), gameTiming);
}
}
}

View File

@@ -1,51 +0,0 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Cooldown
{
/// <summary>
/// Stores a visual "cooldown" for items, that gets displayed in the hands GUI.
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState]
public sealed partial class ItemCooldownComponent : Component
{
// TODO: access and system setting and dirtying not this funny stuff
private TimeSpan? _cooldownEnd;
private TimeSpan? _cooldownStart;
/// <summary>
/// The time when this cooldown ends.
/// </summary>
/// <remarks>
/// If null, no cooldown is displayed.
/// </remarks>
[ViewVariables, AutoNetworkedField]
public TimeSpan? CooldownEnd
{
get => _cooldownEnd;
set
{
_cooldownEnd = value;
Dirty();
}
}
/// <summary>
/// The time when this cooldown started.
/// </summary>
/// <remarks>
/// If null, no cooldown is displayed.
/// </remarks>
[ViewVariables, AutoNetworkedField]
public TimeSpan? CooldownStart
{
get => _cooldownStart;
set
{
_cooldownStart = value;
Dirty();
}
}
}
}

View File

@@ -1,3 +1,5 @@
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Timing;
using JetBrains.Annotations;
namespace Content.Shared.Interaction.Events;
@@ -11,7 +13,13 @@ public sealed class UseInHandEvent : HandledEntityEventArgs
/// <summary>
/// Entity holding the item in their hand.
/// </summary>
public EntityUid User { get; }
public EntityUid User;
/// <summary>
/// Whether or not to apply a UseDelay when used.
/// Mostly used by the <see cref="ClothingSystem"/> quick-equip to not apply the delay to entities that have the <see cref="UseDelayComponent"/>.
/// </summary>
public bool ApplyDelay = true;
public UseInHandEvent(EntityUid user)
{

View File

@@ -6,7 +6,6 @@ using Content.Shared.Administration.Logs;
using Content.Shared.Administration.Managers;
using Content.Shared.CombatMode;
using Content.Shared.Database;
using Content.Shared.DragDrop;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
@@ -21,7 +20,6 @@ using Content.Shared.Popups;
using Content.Shared.Pulling;
using Content.Shared.Pulling.Components;
using Content.Shared.Tag;
using Content.Shared.Throwing;
using Content.Shared.Timing;
using Content.Shared.Verbs;
using Content.Shared.Wall;
@@ -67,8 +65,7 @@ namespace Content.Shared.Interaction
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
private const CollisionGroup InRangeUnobstructedMask
= CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
public const float InteractionRange = 1.5f;
public const float InteractionRangeSquared = InteractionRange * InteractionRange;
@@ -170,7 +167,6 @@ namespace Content.Shared.Interaction
QueueDel(uid);
}
private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (!ValidateClientInput(session, coords, uid, out var userEntity))
@@ -953,7 +949,7 @@ namespace Content.Shared.Interaction
UseDelayComponent? delayComponent = null;
if (checkUseDelay
&& TryComp(used, out delayComponent)
&& delayComponent.ActiveDelay)
&& _useDelay.IsDelayed((used, delayComponent)))
return false;
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
@@ -977,7 +973,8 @@ namespace Content.Shared.Interaction
return false;
DoContactInteraction(user, used, activateMsg);
_useDelay.BeginDelay(used, delayComponent);
if (delayComponent != null)
_useDelay.TryResetDelay((used, delayComponent));
if (!activateMsg.WasLogged)
_adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
return true;
@@ -1002,7 +999,7 @@ namespace Content.Shared.Interaction
if (checkUseDelay
&& TryComp(used, out delayComponent)
&& delayComponent.ActiveDelay)
&& _useDelay.IsDelayed((used, delayComponent)))
return true; // if the item is on cooldown, we consider this handled.
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
@@ -1016,7 +1013,8 @@ namespace Content.Shared.Interaction
if (useMsg.Handled)
{
DoContactInteraction(user, used, useMsg);
_useDelay.BeginDelay(used, delayComponent);
if (delayComponent != null && useMsg.ApplyDelay)
_useDelay.TryResetDelay((used, delayComponent));
return true;
}

View File

@@ -4,18 +4,13 @@ using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Systems;
using Content.Shared.Popups;
using Content.Shared.Strip.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -31,7 +26,6 @@ public abstract partial class InventorySystem
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[ValidatePrototypeId<ItemSizePrototype>]
@@ -46,41 +40,6 @@ public abstract partial class InventorySystem
SubscribeAllEvent<UseSlotNetworkMessage>(OnUseSlot);
}
protected void QuickEquip(EntityUid uid, ClothingComponent component, UseInHandEvent args)
{
if (!TryComp(args.User, out InventoryComponent? inv) || !HasComp<HandsComponent>(args.User))
return;
foreach (var slotDef in inv.Slots)
{
if (!CanEquip(args.User, uid, slotDef.Name, out _, slotDef, inv))
continue;
if (TryGetSlotEntity(args.User, slotDef.Name, out var slotEntity, inv))
{
// Item in slot has to be quick equipable as well
if (TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
continue;
if (!TryUnequip(args.User, slotDef.Name, true, inventory: inv))
continue;
if (!TryEquip(args.User, uid, slotDef.Name, true, inventory: inv))
continue;
_handsSystem.PickupOrDrop(args.User, slotEntity.Value);
}
else
{
if (!TryEquip(args.User, uid, slotDef.Name, true, inventory: inv))
continue;
}
args.Handled = true;
break;
}
}
private void OnEntRemoved(EntityUid uid, InventoryComponent component, EntRemovedFromContainerMessage args)
{
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))

View File

@@ -1,9 +1,6 @@
using Content.Shared.Mech;
using Content.Shared.Mech.Equipment.Components;
using Content.Shared.Mech.Equipment.Systems;
using Content.Shared.Timing;
using Robust.Shared.Audio;
using System.Linq;
using Content.Shared.Mech.Equipment.Components;
using Content.Shared.Timing;
using Robust.Shared.Audio.Systems;
namespace Content.Shared.Mech.Equipment.Systems;
@@ -47,12 +44,11 @@ public sealed class MechSoundboardSystem : EntitySystem
if (msg.Sound >= comp.Sounds.Count)
return;
if (_useDelay.ActiveDelay(uid))
if (TryComp(uid, out UseDelayComponent? useDelay)
&& !_useDelay.TryResetDelay((uid, useDelay), true))
return;
// honk!!!!!
var mech = equipment.EquipmentOwner.Value;
_useDelay.BeginDelay(uid);
_audio.PlayPvs(comp.Sounds[msg.Sound], uid);
}
}

View File

@@ -8,7 +8,6 @@ using Content.Shared.Inventory.Events;
using Content.Shared.Ninja.Components;
using Content.Shared.Popups;
using Content.Shared.Research.Components;
using Content.Shared.Timing;
using Content.Shared.Toggleable;
using Robust.Shared.Timing;
@@ -24,7 +23,6 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
[Dependency] protected readonly SharedInteractionSystem Interaction = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
public override void Initialize()
@@ -110,7 +108,6 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
target = args.Target;
return _timing.IsFirstTimePredicted
&& !_combatMode.IsInCombatMode(uid)
&& !_useDelay.ActiveDelay(uid)
&& TryComp<HandsComponent>(uid, out var hands)
&& hands.ActiveHandEntity == null
&& Interaction.InRangeUnobstructed(uid, target);

View File

@@ -15,7 +15,7 @@ public abstract class SharedNinjaSuitSystem : EntitySystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
[Dependency] protected readonly SharedSpaceNinjaSystem _ninja = default!;
[Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
[Dependency] protected readonly StealthClothingSystem StealthClothing = default!;
[Dependency] protected readonly UseDelaySystem UseDelay = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
@@ -112,8 +112,8 @@ public abstract class SharedNinjaSuitSystem : EntitySystem
_audio.PlayPredicted(comp.RevealSound, uid, user);
// all abilities check for a usedelay on the ninja
var useDelay = EnsureComp<UseDelayComponent>(user);
useDelay.Delay = comp.DisableTime;
UseDelay.BeginDelay(user, useDelay);
UseDelay.SetDelay((user, useDelay), comp.DisableTime);
UseDelay.TryResetDelay((user, useDelay));
}
// TODO: modify PowerCellDrain

View File

@@ -1,28 +1,36 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Timing;
/// <summary>
/// Timer that creates a cooldown each time an object is activated/used
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
/// <remarks>
/// Currently it only supports a single delay per entity, this means that for things that have two delay interactions they will share one timer, so this can cause issues. For example, the bible has a delay when opening the storage UI and when applying it's interaction effect, and they share the same delay.
/// </remarks>
[RegisterComponent]
[NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(UseDelaySystem))]
public sealed partial class UseDelayComponent : Component
{
[AutoNetworkedField]
public TimeSpan LastUseTime;
[AutoNetworkedField]
public TimeSpan? DelayEndTime;
[DataField, AutoNetworkedField]
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan Delay = TimeSpan.FromSeconds(1);
/// <summary>
/// When the delay starts.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
public TimeSpan DelayStartTime;
/// <summary>
/// Stores remaining delay pausing (and eventually, serialization).
/// When the delay ends.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
public TimeSpan DelayEndTime;
/// <summary>
/// Default delay time
/// </summary>
[DataField]
public TimeSpan? RemainingDelay;
public bool ActiveDelay => DelayEndTime != null;
[ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
public TimeSpan Delay = TimeSpan.FromSeconds(1);
}

View File

@@ -1,125 +1,64 @@
using Content.Shared.Cooldown;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Shared.Timing;
public sealed class UseDelaySystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
private HashSet<UseDelayComponent> _activeDelays = new();
[Dependency] private readonly MetaDataSystem _metadata = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<UseDelayComponent, AfterAutoHandleStateEvent>(OnHandleState);
SubscribeLocalEvent<UseDelayComponent, EntityPausedEvent>(OnPaused);
SubscribeLocalEvent<UseDelayComponent, EntityUnpausedEvent>(OnUnpaused);
}
private void OnPaused(EntityUid uid, UseDelayComponent component, ref EntityPausedEvent args)
private void OnUnpaused(Entity<UseDelayComponent> ent, ref EntityUnpausedEvent args)
{
// This entity just got paused, but wasn't before
if (component.DelayEndTime != null)
component.RemainingDelay = _gameTiming.CurTime - component.DelayEndTime;
_activeDelays.Remove(component);
Dirty(component);
// We got unpaused, resume the delay
ent.Comp.DelayStartTime += args.PausedTime;
ent.Comp.DelayEndTime += args.PausedTime;
Dirty(ent);
}
private void OnUnpaused(EntityUid uid, UseDelayComponent component, ref EntityUnpausedEvent args)
public void SetDelay(Entity<UseDelayComponent> ent, TimeSpan delay)
{
if (component.RemainingDelay == null)
if (ent.Comp.Delay == delay)
return;
// We got unpaused, resume the delay/cooldown. Currently this takes for granted that ItemCooldownComponent
// handles the pausing on its own. I'm not even gonna check, because I CBF fixing it if it doesn't.
component.DelayEndTime = _gameTiming.CurTime + component.RemainingDelay;
Dirty(component);
_activeDelays.Add(component);
}
private void OnHandleState(EntityUid uid, UseDelayComponent component, ref AfterAutoHandleStateEvent args)
{
if (component.DelayEndTime == null)
_activeDelays.Remove(component);
else
_activeDelays.Add(component);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var toRemove = new RemQueue<UseDelayComponent>();
var curTime = _gameTiming.CurTime;
var mQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
// TODO refactor this to use active components
foreach (var delay in _activeDelays)
{
if (delay.DelayEndTime == null ||
curTime > delay.DelayEndTime ||
Deleted(delay.Owner, mQuery))
{
toRemove.Add(delay);
}
}
foreach (var delay in toRemove)
{
delay.DelayEndTime = null;
_activeDelays.Remove(delay);
Dirty(delay);
}
ent.Comp.Delay += delay;
Dirty(ent);
}
/// <summary>
/// Attempts tp start a use-delay for some entity. Returns true unless there is already an active delay.
/// Returns true if the entity has a currently active UseDelay.
/// </summary>
/// <remarks>
/// Note that this will always return true if the entity does not have a use delay component, as in that case there
/// is no reason to block/prevent an interaction.
/// </remarks>
public bool BeginDelay(EntityUid uid, UseDelayComponent? component = null)
public bool IsDelayed(Entity<UseDelayComponent> ent)
{
if (!Resolve(uid, ref component, false))
return true;
return ent.Comp.DelayEndTime >= _gameTiming.CurTime;
}
if (component.ActiveDelay)
/// <summary>
/// Cancels the current delay.
/// </summary>
public void CancelDelay(Entity<UseDelayComponent> ent)
{
ent.Comp.DelayEndTime = _gameTiming.CurTime;
Dirty(ent);
}
/// <summary>
/// Resets the UseDelay entirely for this entity if possible.
/// </summary>
/// <param name="checkDelayed">Check if the entity has an ongoing delay, return false if it does, return true if it does not.</param>
public bool TryResetDelay(Entity<UseDelayComponent> ent, bool checkDelayed = false)
{
if (checkDelayed && IsDelayed(ent))
return false;
DebugTools.Assert(!_activeDelays.Contains(component));
_activeDelays.Add(component);
var currentTime = _gameTiming.CurTime;
component.LastUseTime = currentTime;
component.DelayEndTime = currentTime + component.Delay;
Dirty(uid, component);
var cooldown = EnsureComp<ItemCooldownComponent>(uid);
cooldown.CooldownStart = currentTime;
cooldown.CooldownEnd = component.DelayEndTime;
var curTime = _gameTiming.CurTime;
ent.Comp.DelayStartTime = curTime;
ent.Comp.DelayEndTime = curTime - _metadata.GetPauseTime(ent) + ent.Comp.Delay;
Dirty(ent);
return true;
}
public bool ActiveDelay(EntityUid uid, UseDelayComponent? component = null)
{
return Resolve(uid, ref component, false) && component.ActiveDelay;
}
public void Cancel(UseDelayComponent component)
{
component.DelayEndTime = null;
_activeDelays.Remove(component);
Dirty(component);
if (TryComp<ItemCooldownComponent>(component.Owner, out var cooldown))
{
cooldown.CooldownEnd = _gameTiming.CurTime;
}
}
}

View File

@@ -6,10 +6,8 @@ using Content.Shared.Interaction;
using Content.Shared.Movement.Events;
using Content.Shared.Physics;
using Content.Shared.Projectiles;
using Content.Shared.Timing;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Physics;
@@ -30,7 +28,6 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedJointSystem _joints = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly UseDelaySystem _delay = default!;
public const string GrapplingJoint = "grappling";
@@ -117,10 +114,9 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
private void OnGunActivate(EntityUid uid, GrapplingGunComponent component, ActivateInWorldEvent args)
{
if (!Timing.IsFirstTimePredicted || _delay.ActiveDelay(uid))
if (!Timing.IsFirstTimePredicted || args.Handled)
return;
_delay.BeginDelay(uid);
_audio.PlayPredicted(component.CycleSound, uid, args.User);
TryComp<AppearanceComponent>(uid, out var appearance);
@@ -137,6 +133,8 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
component.Projectile = null;
Dirty(uid, component);
}
args.Handled = true;
}
private void SetReeling(EntityUid uid, GrapplingGunComponent component, bool value, EntityUid? user)

View File

@@ -15,6 +15,7 @@ public sealed class UseDelayOnShootSystem : EntitySystem
private void OnUseShoot(EntityUid uid, UseDelayOnShootComponent component, ref GunShotEvent args)
{
_delay.BeginDelay(uid);
if (TryComp(uid, out UseDelayComponent? useDelay))
_delay.TryResetDelay((uid, useDelay));
}
}

View File

@@ -1,27 +1,24 @@
using Content.Shared.DoAfter;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Timing;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Content.Shared.Timing;
namespace Content.Shared.Wieldable;
public sealed class WieldableSystem : EntitySystem
{
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedHandVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
@@ -120,18 +117,18 @@ public sealed class WieldableSystem : EntitySystem
if (args.Handled)
return;
if(!component.Wielded)
if (!component.Wielded)
args.Handled = TryWield(uid, component, args.User);
else
args.Handled = TryUnwield(uid, component, args.User);
}
public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet=false)
public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet = false)
{
// Do they have enough hands free?
if (!EntityManager.TryGetComponent<HandsComponent>(user, out var hands))
{
if(!quiet)
if (!quiet)
_popupSystem.PopupClient(Loc.GetString("wieldable-component-no-hands"), user, user);
return false;
}
@@ -190,10 +187,12 @@ public sealed class WieldableSystem : EntitySystem
_virtualItemSystem.TrySpawnVirtualItemInHand(used, user);
}
_delay.BeginDelay(used);
if (TryComp(used, out UseDelayComponent? useDelay)
&& !_delay.TryResetDelay((used, useDelay), true))
return false;
_popupSystem.PopupClient(Loc.GetString("wieldable-component-successful-wield", ("item", used)), user, user);
_popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield-other", ("user", user),("item", used)), user, Filter.PvsExcept(user), true);
_popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield-other", ("user", user), ("item", used)), user, Filter.PvsExcept(user), true);
var targEv = new ItemWieldedEvent();
RaiseLocalEvent(used, ref targEv);
@@ -224,6 +223,7 @@ public sealed class WieldableSystem : EntitySystem
{
if (args.User == null)
return;
if (!component.Wielded)
return;
@@ -255,6 +255,7 @@ public sealed class WieldableSystem : EntitySystem
{
if (!component.Wielded || uid != args.Unequipped)
return;
RaiseLocalEvent(uid, new ItemUnwieldedEvent(args.User, force: true), true);
}
@@ -268,6 +269,7 @@ public sealed class WieldableSystem : EntitySystem
{
if (!TryComp<WieldableComponent>(uid, out var wield))
return;
if (!wield.Wielded)
return;