using Content.Shared.FixedPoint; using Robust.Shared.Serialization; namespace Content.Shared.DoAfter; [Serializable, NetSerializable] [DataDefinition] public sealed partial class DoAfterArgs { /// /// The entity invoking do_after /// [NonSerialized] [DataField("user", required: true)] public EntityUid User; public NetEntity NetUser; /// /// How long does the do_after require to complete /// [DataField(required: true)] public TimeSpan Delay; /// /// Applicable target (if relevant) /// [NonSerialized] [DataField] public EntityUid? Target; public NetEntity? NetTarget; /// /// Entity used by the User on the Target. /// [NonSerialized] [DataField("using")] public EntityUid? Used; public NetEntity? NetUsed; /// /// Whether the progress bar for this DoAfter should be hidden from other players. /// [DataField] public bool Hidden; #region Event options /// /// The event that will get raised when the DoAfter has finished. If null, this will simply raise a /// [DataField(required: true)] public DoAfterEvent Event = default!; /// /// This option determines how frequently the DoAfterAttempt event will get raised. Defaults to never raising the /// event. /// [DataField("attemptEventFrequency")] public AttemptFrequency AttemptFrequency; /// /// Entity which will receive the directed event. If null, no directed event will be raised. /// [NonSerialized] [DataField] public EntityUid? EventTarget; public NetEntity? NetEventTarget; /// /// Should the DoAfter event broadcast? If this is false, then should be a valid entity. /// [DataField] public bool Broadcast; #endregion #region Break/Cancellation Options // Break the chains /// /// Whether or not this do after requires the user to have hands. /// [DataField] public bool NeedHand; /// /// Whether we need to keep our active hand as is (i.e. can't change hand or change item). This also covers /// requiring the hand to be free (if applicable). This does nothing if is false. /// [DataField] public bool BreakOnHandChange = true; /// /// Whether the do-after should get interrupted if we drop the /// active item we started the do-after with /// This does nothing if is false. /// [DataField] public bool BreakOnDropItem = true; /// /// If do_after stops when the user or target moves /// [DataField] public bool BreakOnMove; /// /// Whether to break on movement when the user is weightless. /// This does nothing if is false. /// [DataField] public bool BreakOnWeightlessMove = true; /// /// Threshold for user and target movement /// [DataField] public float MovementThreshold = 0.3f; /// /// Threshold for distance user from the used OR target entities. /// [DataField] public float? DistanceThreshold = 1.5f; /// /// Whether damage will cancel the DoAfter. See also . /// [DataField] public bool BreakOnDamage; /// /// Threshold for user damage. This damage has to be dealt in a single event, not over time. /// [DataField] public FixedPoint2 DamageThreshold = 1; /// /// If true, this DoAfter will be canceled if the user can no longer interact with the target. /// [DataField] public bool RequireCanInteract = true; #endregion #region Duplicates /// /// If true, this will prevent duplicate DoAfters from being started See also . /// /// /// Note that this will block even if the duplicate is cancelled because either DoAfter had /// enabled. /// [DataField] public bool BlockDuplicate = true; //TODO: User pref to not cancel on second use on specific doafters /// /// If true, this will cancel any duplicate DoAfters when attempting to add a new DoAfter. See also /// . /// [DataField] public bool CancelDuplicate = true; /// /// These flags determine what DoAfter properties are used to determine whether one DoAfter is a duplicate of /// another. /// /// /// Note that both DoAfters may have their own conditions, and they will be considered duplicated if either set /// of conditions is satisfied. /// [DataField] public DuplicateConditions DuplicateCondition = DuplicateConditions.All; #endregion /// /// Additional conditions that need to be met. Return false to cancel. /// [NonSerialized] [Obsolete("Use checkEvent instead")] public Func? ExtraCheck; #region Constructors /// /// Creates a new set of DoAfter arguments. /// /// The user that will perform the DoAfter /// The time it takes for the DoAfter to complete /// The event that will be raised when the DoAfter has ended (completed or cancelled). /// The entity at which the event will be directed. If null, the event will not be directed. /// The entity being targeted by the DoAFter. Not the same as . /// The entity being used during the DoAfter. E.g., a tool public DoAfterArgs( IEntityManager entManager, EntityUid user, TimeSpan delay, DoAfterEvent @event, EntityUid? eventTarget, EntityUid? target = null, EntityUid? used = null) { User = user; Delay = delay; Target = target; Used = used; EventTarget = eventTarget; Event = @event; NetUser = entManager.GetNetEntity(User); NetTarget = entManager.GetNetEntity(Target); NetUsed = entManager.GetNetEntity(Used); } private DoAfterArgs() { } /// /// Creates a new set of DoAfter arguments. /// /// The user that will perform the DoAfter /// The time it takes for the DoAfter to complete, in seconds /// The event that will be raised when the DoAfter has ended (completed or cancelled). /// The entity at which the event will be directed. If null, the event will not be directed. /// The entity being targeted by the DoAfter. Not the same as . /// The entity being used during the DoAfter. E.g., a tool public DoAfterArgs( IEntityManager entManager, EntityUid user, float seconds, DoAfterEvent @event, EntityUid? eventTarget, EntityUid? target = null, EntityUid? used = null) : this(entManager, user, TimeSpan.FromSeconds(seconds), @event, eventTarget, target, used) { } #endregion //The almighty pyramid returns....... public DoAfterArgs(DoAfterArgs other) { User = other.User; Delay = other.Delay; Target = other.Target; Used = other.Used; Hidden = other.Hidden; EventTarget = other.EventTarget; Broadcast = other.Broadcast; NeedHand = other.NeedHand; BreakOnHandChange = other.BreakOnHandChange; BreakOnDropItem = other.BreakOnDropItem; BreakOnMove = other.BreakOnMove; BreakOnWeightlessMove = other.BreakOnWeightlessMove; MovementThreshold = other.MovementThreshold; DistanceThreshold = other.DistanceThreshold; BreakOnDamage = other.BreakOnDamage; DamageThreshold = other.DamageThreshold; RequireCanInteract = other.RequireCanInteract; AttemptFrequency = other.AttemptFrequency; BlockDuplicate = other.BlockDuplicate; CancelDuplicate = other.CancelDuplicate; DuplicateCondition = other.DuplicateCondition; // Networked NetUser = other.NetUser; NetTarget = other.NetTarget; NetUsed = other.NetUsed; NetEventTarget = other.NetEventTarget; Event = other.Event.Clone(); } } /// /// See . /// [Flags] public enum DuplicateConditions : byte { /// /// This DoAfter will consider any other DoAfter with the same user to be a duplicate. /// None = 0, /// /// Requires that refers to the same entity in order to be considered a duplicate. /// /// /// E.g., if all checks are enabled for stripping, then stripping different articles of clothing on the same /// mob would be allowed. If instead this check were disabled, then any stripping actions on the same target /// would be considered duplicates, so you would only be able to take one piece of clothing at a time. /// SameTool = 1 << 1, /// /// Requires that refers to the same entity in order to be considered a duplicate. /// /// /// E.g., if all checks are enabled for mining, then using the same pickaxe to mine different rocks will be /// allowed. If instead this check were disabled, then the trying to mine a different rock with the same /// pickaxe would be considered a duplicate DoAfter. /// SameTarget = 1 << 2, /// /// Requires that the types match in order to be considered a duplicate. /// /// /// If your DoAfter should block other unrelated DoAfters involving the same set of entities, you may want /// to disable this condition. E.g. force feeding a donk pocket and forcefully giving someone a donk pocket /// should be mutually exclusive, even though the DoAfters have unrelated effects. /// SameEvent = 1 << 3, All = SameTool | SameTarget | SameEvent, } public enum AttemptFrequency : byte { /// /// Never raise the attempt event. /// Never = 0, /// /// Raises the attempt event when the DoAfter is about to start or end. /// StartAndEnd = 1, /// /// Raise the attempt event every tick while the DoAfter is running. /// EveryTick = 2 }