UseDelay + ItemCooldown merge (#22502)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user