diff --git a/EXILED/Exiled.API/Enums/LockerType.cs b/EXILED/Exiled.API/Enums/LockerType.cs index ca176fb21..b38b592c0 100644 --- a/EXILED/Exiled.API/Enums/LockerType.cs +++ b/EXILED/Exiled.API/Enums/LockerType.cs @@ -7,6 +7,8 @@ namespace Exiled.API.Enums { + using System; + /// /// Unique identifier for different types of s. /// @@ -15,6 +17,7 @@ public enum LockerType /// /// The pedestal used by SCP items. /// + [Obsolete("This value is not used.")] Pedestal, /// @@ -46,5 +49,65 @@ public enum LockerType /// Unknow type of locker. /// Unknow, + + /// + /// MircoHid pedestal. + /// + MicroHid, + + /// + /// Experimental weapon locker. + /// + ExperimentalWeapon, + + /// + /// SCP-500 pedestal. + /// + Scp500Pedestal, + + /// + /// SCP-207? (Anti SCP-207) pedestal. + /// + AntiScp207Pedestal, + + /// + /// SCP-207 pedestal. + /// + Scp207Pedestal, + + /// + /// SCP-268 pedestal. + /// + Scp268Pedestal, + + /// + /// SCP-1344 pedestal. + /// + Scp1344Pedestal, + + /// + /// SCP-018 pedestal. + /// + Scp018Pedestal, + + /// + /// SCP-1576 pedestal. + /// + Scp1576Pedestal, + + /// + /// SCP-244 pedestal. + /// + Scp244Pedestal, + + /// + /// SCP-2176 pedestal. + /// + Scp2176Pedestal, + + /// + /// SCP-1853 pedestal. + /// + Scp1853Pedestal, } } diff --git a/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs index d26328475..38ef020b7 100644 --- a/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs +++ b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs @@ -59,5 +59,21 @@ public static T ModifyFlags(this T flags, bool value, params T[] changeFlags) return (T)Enum.ToObject(typeof(T), currentValue); } + + /// + /// Checks if flag has specified value. + /// + /// Flag to check. + /// Value to check in flag. + /// The type of the enum. + /// if value is presented in flag. Otherwise, . + public static bool HasFlagFast(this T flag, T value) + where T : Enum + { + long flagValue = Convert.ToInt64(flag); + long valueValue = Convert.ToInt64(value); + + return (flagValue & valueValue) == valueValue; + } } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Extensions/LockerExtensions.cs b/EXILED/Exiled.API/Extensions/LockerExtensions.cs index c7c1a78ab..bdf3689bb 100644 --- a/EXILED/Exiled.API/Extensions/LockerExtensions.cs +++ b/EXILED/Exiled.API/Extensions/LockerExtensions.cs @@ -29,14 +29,25 @@ public static class LockerExtensions /// /// The name to check. /// The corresponding . - public static LockerType GetLockerTypeByName(this string name) => name.Replace("(Clone)", string.Empty) switch + public static LockerType GetLockerTypeByName(this string name) => name.Split('(')[0].Trim() switch { - "Scp500PedestalStructure Variant" => LockerType.Pedestal, + "Scp500PedestalStructure Variant" => LockerType.Scp500Pedestal, + "AntiScp207PedestalStructure Variant" => LockerType.AntiScp207Pedestal, + "Scp207PedestalStructure Variant" => LockerType.Scp207Pedestal, + "Experimental Weapon Locker" => LockerType.ExperimentalWeapon, + "Scp1344PedestalStructure Variant" => LockerType.Scp1344Pedestal, + "Scp1576PedestalStructure Variant" => LockerType.Scp1576Pedestal, + "Scp2176PedestalStructure Variant" => LockerType.Scp2176Pedestal, + "Scp1853PedestalStructure Variant" => LockerType.Scp1853Pedestal, + "Scp268PedestalStructure Variant" => LockerType.Scp268Pedestal, + "Scp244PedestalStructure Variant" => LockerType.Scp244Pedestal, + "Scp018PedestalStructure Variant" => LockerType.Scp018Pedestal, "LargeGunLockerStructure" => LockerType.LargeGun, "RifleRackStructure" => LockerType.RifleRack, "MiscLocker" => LockerType.Misc, "RegularMedkitStructure" => LockerType.Medkit, "AdrenalineMedkitStructure" => LockerType.Adrenaline, + "MicroHIDpedestal" => LockerType.MicroHid, _ => LockerType.Unknow, }; } diff --git a/EXILED/Exiled.API/Features/CustomHealthStat.cs b/EXILED/Exiled.API/Features/CustomHealthStat.cs index af351bf7a..57f6d31c2 100644 --- a/EXILED/Exiled.API/Features/CustomHealthStat.cs +++ b/EXILED/Exiled.API/Features/CustomHealthStat.cs @@ -11,6 +11,7 @@ namespace Exiled.API.Features /// /// A custom version of which allows the player's max amount of health to be changed. + /// TODO: Move to Features.CustomStats. /// public class CustomHealthStat : HealthStat { diff --git a/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs b/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs new file mode 100644 index 000000000..78c4cd807 --- /dev/null +++ b/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs @@ -0,0 +1,79 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.CustomStats +{ + using Mirror; + using PlayerRoles.PlayableScps.HumeShield; + using PlayerStatsSystem; + using UnityEngine; + using Utils.Networking; + + /// + /// A custom version of which allows the player's max amount of HumeShield to be changed. + /// + public class CustomHumeShieldStat : HumeShieldStat + { + /// + public override float MaxValue => CustomMaxValue == -1 ? base.MaxValue : CustomMaxValue; + + /// + /// Gets or sets the multiplier for gaining HumeShield. + /// + public float ShieldRegenerationMultiplier { get; set; } = 1; + + /// + /// Gets or sets the maximum amount of HumeShield the player can have. + /// + public float CustomMaxValue { get; set; } = -1; + + private float ShieldRegeneration => TryGetHsModule(out HumeShieldModuleBase controller) ? controller.HsRegeneration * ShieldRegenerationMultiplier : 0; + + /// + public override void Update() + { + if (MaxValue == -1 && ShieldRegenerationMultiplier is 1) + { + base.Update(); + return; + } + + if (!NetworkServer.active) + return; + + if (_valueDirty) + { + new SyncedStatMessages.StatMessage() + { + Stat = this, + SyncedValue = CurValue, + }.SendToHubsConditionally(CanReceive); + _lastSent = CurValue; + _valueDirty = false; + } + + if (ShieldRegeneration == 0) + return; + + float delta = ShieldRegeneration * Time.deltaTime; + + if (delta > 0) + { + if (CurValue >= MaxValue) + return; + + CurValue = Mathf.MoveTowards(CurValue, MaxValue, delta); + return; + } + + if (CurValue <= 0) + return; + + CurValue += delta; + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs index 63b4f62ce..c5ba62fac 100644 --- a/EXILED/Exiled.API/Features/Doors/Door.cs +++ b/EXILED/Exiled.API/Features/Doors/Door.cs @@ -627,7 +627,13 @@ private DoorType GetDoorType() // Doors contains the DoorNameTagExtension component "CHECKPOINT_LCZ_A" => DoorType.CheckpointLczA, "CHECKPOINT_LCZ_B" => DoorType.CheckpointLczB, - "CHECKPOINT_EZ_HCZ_A" => DoorType.CheckpointEzHczA, + + // TODO: Remove when it's fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/782 + "CHECKPOINT_EZ_HCZ_A" => Room?.Type switch + { + RoomType.HczEzCheckpointA => DoorType.CheckpointEzHczA, + _ => DoorType.CheckpointEzHczB, + }, "CHECKPOINT_EZ_HCZ_B" => DoorType.CheckpointEzHczB, "106_PRIMARY" => DoorType.Scp106Primary, "106_SECONDARY" => DoorType.Scp106Secondary, diff --git a/EXILED/Exiled.API/Features/Lift.cs b/EXILED/Exiled.API/Features/Lift.cs index c38e146a3..ec2a87c84 100644 --- a/EXILED/Exiled.API/Features/Lift.cs +++ b/EXILED/Exiled.API/Features/Lift.cs @@ -223,6 +223,13 @@ public float AnimationTime /// A or if not found. public static Lift Get(ElevatorType type) => Get(lift => lift.Type == type).FirstOrDefault(); + /// + /// Gets the corresponding to the specified , if any. + /// + /// The . + /// A or if not found. + public static Lift Get(ElevatorGroup type) => Get(lift => lift.Group == type).FirstOrDefault(); + /// /// Gets the corresponding to the specified name, if any. /// diff --git a/EXILED/Exiled.API/Features/Lockers/Locker.cs b/EXILED/Exiled.API/Features/Lockers/Locker.cs index 68fe4a4f1..653e432d2 100644 --- a/EXILED/Exiled.API/Features/Lockers/Locker.cs +++ b/EXILED/Exiled.API/Features/Lockers/Locker.cs @@ -110,7 +110,7 @@ public Vector3 RandomChamberPosition Chamber randomChamber = Chambers.GetRandomValue(); // Determine if the chamber uses multiple spawn points and has at least one available spawn point. - if (randomChamber.UseMultipleSpawnpoints && randomChamber.Spawnpoints.Count() > 0) + if (randomChamber.UseMultipleSpawnpoints && randomChamber.Spawnpoints.Any()) { // Return the position of a random spawn point within the chamber. return randomChamber.Spawnpoints.GetRandomValue().position; @@ -136,11 +136,18 @@ public Vector3 RandomChamberPosition /// The with the given or if not found. public static IEnumerable Get(ZoneType zoneType) => Get(room => room.Zone.HasFlag(zoneType)); + /// + /// Gets an of given the specified . + /// + /// The to search for. + /// An of which contains elements that satisfy the condition. + public static IEnumerable Get(LockerType lockerType) => Get(x => x.Type == lockerType); + /// /// Gets a of filtered based on a predicate. /// /// The condition to satify. - /// A of which contains elements that satify the condition. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 1c1e7fb54..c0bc8f40b 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -19,6 +19,7 @@ namespace Exiled.API.Features using DamageHandlers; using Enums; using Exiled.API.Features.Core.Interfaces; + using Exiled.API.Features.CustomStats; using Exiled.API.Features.Doors; using Exiled.API.Features.Hazards; using Exiled.API.Features.Items; @@ -96,6 +97,7 @@ public class Player : TypeCastObject, IEntity, IWorldSpace private ReferenceHub referenceHub; private CustomHealthStat healthStat; + private CustomHumeShieldStat humeShieldStat; private Role role; /// @@ -177,6 +179,7 @@ private set CameraTransform = value.PlayerCameraReference; value.playerStats._dictionarizedTypes[typeof(HealthStat)] = value.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HealthStat))] = healthStat = new CustomHealthStat { Hub = value }; + value.playerStats._dictionarizedTypes[typeof(HumeShieldStat)] = value.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HumeShieldStat))] = humeShieldStat = new CustomHumeShieldStat { Hub = value }; } } @@ -930,6 +933,24 @@ public float HumeShield set => HumeShieldStat.CurValue = value; } + /// + /// Gets or sets the players maximum Hume Shield. + /// + public float MaxHumeShield + { + get => humeShieldStat.MaxValue; + set => humeShieldStat.CustomMaxValue = value; + } + + /// + /// Gets or sets the players multiplier for gaining HumeShield. + /// + public float HumeShieldRegenerationMultiplier + { + get => humeShieldStat.ShieldRegenerationMultiplier; + set => humeShieldStat.ShieldRegenerationMultiplier = value; + } + /// /// Gets a of all active Artificial Health processes on the player. /// @@ -937,8 +958,9 @@ public float HumeShield /// /// Gets the player's . + /// TODO: Change to . /// - public HumeShieldStat HumeShieldStat => ReferenceHub.playerStats.GetModule(); + public HumeShieldStat HumeShieldStat => humeShieldStat; /// /// Gets or sets the item in the player's hand. Value will be if the player is not holding anything. diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs index 098340fa7..dc880fe69 100644 --- a/EXILED/Exiled.API/Features/Room.cs +++ b/EXILED/Exiled.API/Features/Room.cs @@ -508,12 +508,12 @@ private static RoomType FindType(GameObject gameObject) "EZ_Shelter" => RoomType.EzShelter, "EZ_HCZ_Checkpoint Part" => gameObject.transform.position.z switch { - > 80 => RoomType.EzCheckpointHallwayA, + > 95 => RoomType.EzCheckpointHallwayA, _ => RoomType.EzCheckpointHallwayB, }, "HCZ_EZ_Checkpoint Part" => gameObject.transform.position.z switch { - > 80 => RoomType.HczEzCheckpointA, + > 95 => RoomType.HczEzCheckpointA, _ => RoomType.HczEzCheckpointB }, _ => RoomType.Unknown, diff --git a/EXILED/Exiled.API/Features/Toys/Light.cs b/EXILED/Exiled.API/Features/Toys/Light.cs index eee626928..9496f6b83 100644 --- a/EXILED/Exiled.API/Features/Toys/Light.cs +++ b/EXILED/Exiled.API/Features/Toys/Light.cs @@ -113,6 +113,15 @@ public LightType LightType set => Base.NetworkLightType = value; } + /// + /// Gets or sets the type of shadows the light casts. + /// + public LightShadows ShadowType + { + get => Base.NetworkShadowType; + set => Base.NetworkShadowType = value; + } + /// /// Creates a new . /// diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs index f4f6116ae..8aff47f91 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs @@ -834,7 +834,8 @@ internal bool TryUnregister() protected virtual void SubscribeEvents() { Exiled.Events.Handlers.Player.Dying += OnInternalOwnerDying; - Exiled.Events.Handlers.Player.DroppingItem += OnInternalDropping; + Exiled.Events.Handlers.Player.DroppingItem += OnInternalDroppingItem; + Exiled.Events.Handlers.Player.DroppingAmmo += OnInternalDroppingAmmo; Exiled.Events.Handlers.Player.ChangingItem += OnInternalChanging; Exiled.Events.Handlers.Player.Escaping += OnInternalOwnerEscaping; Exiled.Events.Handlers.Player.PickingUpItem += OnInternalPickingUp; @@ -852,7 +853,8 @@ protected virtual void SubscribeEvents() protected virtual void UnsubscribeEvents() { Exiled.Events.Handlers.Player.Dying -= OnInternalOwnerDying; - Exiled.Events.Handlers.Player.DroppingItem -= OnInternalDropping; + Exiled.Events.Handlers.Player.DroppingItem -= OnInternalDroppingItem; + Exiled.Events.Handlers.Player.DroppingAmmo -= OnInternalDroppingAmmo; Exiled.Events.Handlers.Player.ChangingItem -= OnInternalChanging; Exiled.Events.Handlers.Player.Escaping -= OnInternalOwnerEscaping; Exiled.Events.Handlers.Player.PickingUpItem -= OnInternalPickingUp; @@ -900,10 +902,27 @@ protected virtual void OnOwnerHandcuffing(OwnerHandcuffingEventArgs ev) /// Handles tracking items when they are dropped by a player. /// /// . + protected virtual void OnDroppingItem(DroppingItemEventArgs ev) + { + } + + /// + /// Handles tracking items when they are dropped by a player. + /// + /// . + [Obsolete("Use OnDroppingItem instead.", false)] protected virtual void OnDropping(DroppingItemEventArgs ev) { } + /// + /// Handles tracking when player requests drop of item which equals to the specified by . + /// + /// . + protected virtual void OnDroppingAmmo(DroppingAmmoEventArgs ev) + { + } + /// /// Handles tracking items when they are picked up by a player. /// @@ -1061,12 +1080,25 @@ private void OnInternalOwnerHandcuffing(HandcuffingEventArgs ev) } } - private void OnInternalDropping(DroppingItemEventArgs ev) + private void OnInternalDroppingItem(DroppingItemEventArgs ev) { if (!Check(ev.Item)) return; + OnDroppingItem(ev); + + // TODO: Don't forget to remove this with next update +#pragma warning disable CS0618 OnDropping(ev); +#pragma warning restore CS0618 + } + + private void OnInternalDroppingAmmo(DroppingAmmoEventArgs ev) + { + if (Type != ev.ItemType) + return; + + OnDroppingAmmo(ev); } private void OnInternalPickingUp(PickingUpItemEventArgs ev) diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs b/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs new file mode 100644 index 000000000..e28f1ea3d --- /dev/null +++ b/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs @@ -0,0 +1,127 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomItems.API.Features +{ + using System; + + using Exiled.API.Enums; + using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Doors; + using Exiled.API.Features.Items; + using Exiled.API.Features.Lockers; + using Exiled.API.Features.Pickups; + using Exiled.Events.EventArgs.Item; + using Exiled.Events.EventArgs.Player; + using UnityEngine; + + /// + /// The Custom keycard base class. + /// + public abstract class CustomKeycard : CustomItem + { + /// + /// Throws if specified is not Keycard. + public override ItemType Type + { + get => base.Type; + set + { + if (!value.IsKeycard()) + throw new ArgumentOutOfRangeException("Type", value, "Invalid keycard type."); + + base.Type = value; + } + } + + /// + /// Gets or sets the permissions for custom keycard. + /// + public virtual KeycardPermissions Permissions { get; set; } + + /// + public override void Give(Player player, Item item, bool displayMessage = true) + { + base.Give(player, item, displayMessage); + + if (item.Is(out Keycard card)) + card.Permissions = Permissions; + } + + /// + public override Pickup? Spawn(Vector3 position, Item item, Player? previousOwner = null) + { + if (item.Is(out Keycard card)) + card.Permissions = Permissions; + + return base.Spawn(position, item, previousOwner); + } + + /// + /// Called when custom keycard interacts with a door. + /// + /// Owner of Custom keycard. + /// Door with which interacting. + protected virtual void OnInteractingDoor(Player player, Door door) + { + } + + /// + /// Called when custom keycard interacts with a locker. + /// + /// Owner of Custom keycard. + /// Chamber with which interacting. + protected virtual void OnInteractingLocker(Player player, Chamber chamber) + { + } + + /// + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + + Exiled.Events.Handlers.Player.InteractingDoor += OnInternalInteractingDoor; + Exiled.Events.Handlers.Player.InteractingLocker += OnInternalInteractingLocker; + Exiled.Events.Handlers.Item.KeycardInteracting += OnInternalKeycardInteracting; + } + + /// + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + + Exiled.Events.Handlers.Player.InteractingDoor -= OnInternalInteractingDoor; + Exiled.Events.Handlers.Player.InteractingLocker -= OnInternalInteractingLocker; + Exiled.Events.Handlers.Item.KeycardInteracting -= OnInternalKeycardInteracting; + } + + private void OnInternalKeycardInteracting(KeycardInteractingEventArgs ev) + { + if (!Check(ev.Pickup)) + return; + + OnInteractingDoor(ev.Player, ev.Door); + } + + private void OnInternalInteractingDoor(InteractingDoorEventArgs ev) + { + if (!Check(ev.Player.CurrentItem)) + return; + + OnInteractingDoor(ev.Player, ev.Door); + } + + private void OnInternalInteractingLocker(InteractingLockerEventArgs ev) + { + if (!Check(ev.Player.CurrentItem)) + return; + + OnInteractingLocker(ev.Player, ev.InteractingChamber); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs index b8217fb41..7de3e2eef 100644 --- a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs +++ b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs @@ -920,7 +920,7 @@ private void OnInternalChangingNickname(ChangingNicknameEventArgs ev) private void OnInternalSpawning(SpawningEventArgs ev) { - if (!IgnoreSpawnSystem && SpawnChance > 0 && !Check(ev.Player) && ev.Player.Role.Type == Role && Loader.Random.NextDouble() * 100 <= SpawnChance) + if (!IgnoreSpawnSystem && SpawnChance > 0 && !Check(ev.Player) && ev.NewRole == Role && Loader.Random.NextDouble() * 100 <= SpawnChance) AddRole(ev.Player); } diff --git a/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs index 342d4b311..6bb6c9ad6 100644 --- a/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs @@ -26,9 +26,7 @@ public ChargingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.Item { Player = Player.Get(player); Jailbird = (Jailbird)Item.Get(swingItem); -#pragma warning disable CS0618 IsAllowed = isAllowed; -#pragma warning restore CS0618 } /// @@ -47,8 +45,8 @@ public ChargingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.Item public Item Item => Jailbird; /// - /// Gets a value indicating whether the Jailbird can be charged. + /// Gets or sets a value indicating whether the Jailbird can be charged. /// - public bool IsAllowed { get; } + public bool IsAllowed { get; set; } } } diff --git a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs index 27b0bc165..7cb2613ce 100644 --- a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs @@ -27,6 +27,8 @@ namespace Exiled.Events.EventArgs.Map /// public class ExplodingGrenadeEventArgs : IPlayerEvent, IDeniableEvent { + private ExplosionType explosionType; + /// /// Initializes a new instance of the class. /// @@ -34,12 +36,14 @@ public class ExplodingGrenadeEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionGrenade grenade, Collider[] targets) + /// + public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionGrenade grenade, Collider[] targets, ExplosionType explosionType) { Player = Player.Get(thrower.Hub); Projectile = Pickup.Get(grenade); Position = position; TargetsToAffect = HashSetPool.Pool.Get(); + ExplosionType = explosionType; if (Projectile.Base is not ExplosionGrenade) return; @@ -97,6 +101,7 @@ public ExplodingGrenadeEventArgs(Player thrower, EffectGrenade grenade, HashSet< Player = thrower ?? Server.Host; Projectile = Pickup.Get(grenade); Position = Projectile.Position; + ExplosionType = ExplosionType.Custom; TargetsToAffect = HashSetPool.Pool.Get(targetsToAffect ?? new HashSet()); IsAllowed = isAllowed; } @@ -114,6 +119,16 @@ public ExplodingGrenadeEventArgs(Player thrower, EffectGrenade grenade, HashSet< /// public Vector3 Position { get; } + /// + /// Gets or sets the Explosion type. + /// + /// Explosion that are not from will return and can't be modified. + public ExplosionType ExplosionType + { + get => explosionType; + set => explosionType = Projectile is ExplosionGrenadeProjectile ? value : ExplosionType.Custom; + } + /// /// Gets the players who could be affected by the grenade, if any, and the damage that be dealt. /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/DroppedAmmoEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DroppedAmmoEventArgs.cs index fe2dacbd9..78d229d08 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/DroppedAmmoEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/DroppedAmmoEventArgs.cs @@ -11,6 +11,7 @@ namespace Exiled.Events.EventArgs.Player using API.Enums; using API.Features; + using Exiled.API.Extensions; using Exiled.API.Features.Pickups; using Interfaces; @@ -27,8 +28,8 @@ public class DroppedAmmoEventArgs : IPlayerEvent /// /// /// - /// - /// + /// + /// /// /// /// @@ -36,14 +37,20 @@ public class DroppedAmmoEventArgs : IPlayerEvent /// /// /// - public DroppedAmmoEventArgs(Player player, AmmoType ammoType, ushort amount, List ammoPickups) + public DroppedAmmoEventArgs(Player player, ItemType itemType, ushort amount, List ammoPickups) { Player = player; - AmmoType = ammoType; + ItemType = itemType; + AmmoType = ItemExtensions.GetAmmoType(itemType); Amount = amount; AmmoPickups = Pickup.Get(ammoPickups); } + /// + /// Gets the type of dropped item instead of . + /// + public ItemType ItemType { get; } + /// /// Gets the type of dropped ammo. /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/DroppingAmmoEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DroppingAmmoEventArgs.cs index cceb19e75..0b379c903 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/DroppingAmmoEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/DroppingAmmoEventArgs.cs @@ -9,7 +9,7 @@ namespace Exiled.Events.EventArgs.Player { using API.Enums; using API.Features; - + using Exiled.API.Extensions; using Interfaces; using PlayerRoles; @@ -27,8 +27,8 @@ public class DroppingAmmoEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - /// - /// + /// + /// /// /// /// @@ -36,14 +36,21 @@ public class DroppingAmmoEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - public DroppingAmmoEventArgs(Player player, AmmoType ammoType, ushort amount, bool isAllowed = true) + public DroppingAmmoEventArgs(Player player, ItemType itemType, ushort amount, bool isAllowed = true) { Player = player; - AmmoType = ammoType; + ItemType = itemType; + AmmoType = ItemExtensions.GetAmmoType(itemType); Amount = amount; IsAllowed = isAllowed; } + /// + /// Gets the type of item being dropped instead of . + /// For example, if the plugin gives the player one of the items instead of ammo. + /// + public ItemType ItemType { get; } + /// /// Gets the type of ammo being dropped. /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs index 49a5f2049..75eb562f3 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs @@ -9,6 +9,7 @@ namespace Exiled.Events.EventArgs.Player { using API.Features; using Exiled.API.Features.Doors; + using Interactables; using Interactables.Interobjects.DoorUtils; using Interfaces; @@ -26,16 +27,21 @@ public class InteractingDoorEventArgs : IPlayerEvent, IDoorEvent, IDeniableEvent /// /// /// + /// + /// + /// /// /// /// /// /// /// - public InteractingDoorEventArgs(Player player, DoorVariant door, bool isAllowed = true, bool canInteract = true) + public InteractingDoorEventArgs(Player player, DoorVariant door, byte colliderId, bool isAllowed = true, bool canInteract = true) { Player = player; Door = Door.Get(door); + ColliderId = colliderId; + Collider = InteractableCollider.TryGetCollider(door, colliderId, out InteractableCollider interactableCollider) ? interactableCollider : null; IsAllowed = isAllowed; CanInteract = canInteract; } @@ -51,10 +57,20 @@ public InteractingDoorEventArgs(Player player, DoorVariant door, bool isAllowed public bool IsAllowed { get; set; } /// - /// Gets or sets the instance. + /// Gets or sets the instance. /// public Door Door { get; set; } + /// + /// Gets the instance that the player interacted with. + /// + public InteractableCollider Collider { get; } + + /// + /// Gets the ColliderId of that the player interacted with. + /// + public byte ColliderId { get; } + /// /// Gets the player who's interacting with the door. /// diff --git a/EXILED/Exiled.Events/Patches/Events/Item/JailbirdPatch.cs b/EXILED/Exiled.Events/Patches/Events/Item/JailbirdPatch.cs index af13bdffc..8ffa6e06e 100644 --- a/EXILED/Exiled.Events/Patches/Events/Item/JailbirdPatch.cs +++ b/EXILED/Exiled.Events/Patches/Events/Item/JailbirdPatch.cs @@ -88,7 +88,12 @@ private static bool HandleJailbird(JailbirdItem instance, JailbirdMessageType me ChargingJailbirdEventArgs ev = new(instance.Owner, instance); Item.OnChargingJailbird(ev); - return true; + if (ev.IsAllowed) + return true; + + ev.Player.RemoveHeldItem(destroy: false); + ev.Player.AddItem(ev.Item); + return false; } default: diff --git a/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs b/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs index 37a8bef64..e29c8c621 100644 --- a/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs +++ b/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs @@ -84,8 +84,11 @@ private static IEnumerable Transpiler(IEnumerable /// Patches . /// Adds the event. @@ -50,10 +46,10 @@ private static IEnumerable Transpiler(IEnumerable x.Name == "Get").First()), + new(OpCodes.Call, typeof(Exiled.API.Features.Items.Item).GetMethods().Where(x => x.Name == "Get").First()), // hit - new(OpCodes.Ldarg_2), + new(OpCodes.Ldarg_1), // PlacingBulletHole ev = new(Item, RaycastHit) new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PlacingBulletHoleEventArgs))[0]), @@ -70,13 +66,13 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable x.Calls(PropertyGetter(typeof(AutomaticActionModule), nameof(AutomaticActionModule.Cocked)))) + offset; + int index = newInstructions.FindLastIndex(x => x.Calls(PropertySetter(typeof(AutomaticActionModule), nameof(AutomaticActionModule.Cocked)))) + offset; newInstructions.InsertRange(index, GetInstructions(newInstructions[index], ret)); @@ -78,6 +76,7 @@ private static IEnumerable Transpiler(IEnumerable Commented out, because it is never called (nw moment). Calls 2 PumpActionModule.ShootOneBarrel(bool) instead. /// /// Patches /// to add event for double shots. @@ -102,10 +101,11 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } } + */ /// - /// Patches - /// to add event for double shots. + /// Patches + /// to add event for pump shots. /// [EventPatch(typeof(Player), nameof(Player.DryfiringWeapon))] [HarmonyPatch(typeof(PumpActionModule), nameof(PumpActionModule.ShootOneBarrel))] diff --git a/EXILED/Exiled.Events/Patches/Events/Player/InteractingDoor.cs b/EXILED/Exiled.Events/Patches/Events/Player/InteractingDoor.cs index 25b567ac7..417b03b85 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/InteractingDoor.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/InteractingDoor.cs @@ -44,10 +44,11 @@ private static IEnumerable Transpiler(IEnumerable - /// Patches - /// to add and events. + /// Patches + /// to add and events. /// - [HarmonyPatch(typeof(BanHandler), nameof(BanHandler.RemoveBan))] [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.Unbanning))] [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.Unbanned))] + [HarmonyPatch] internal class Unban { - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + [HarmonyPatch(typeof(BanHandler), nameof(BanHandler.RemoveBan))] + [HarmonyTranspiler] + private static IEnumerable Transpiler( + IEnumerable instructions, + ILGenerator generator) { List newInstructions = ListPool.Pool.Get(instructions); @@ -34,7 +38,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); + } + + [HarmonyPatch(typeof(BanHandler), nameof(BanHandler.ValidateBans), typeof(BanHandler.BanType))] + [HarmonyTranspiler] + private static IEnumerable BanHandlerTranspiler( + IEnumerable instructions, + ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder ev = generator.DeclareLocal(typeof(UnbanningEventArgs)); + + Label continueLabel = generator.DefineLabel(); + + const int offset = 2; + int index = newInstructions.FindIndex(instruction => + instruction.Calls(Method(typeof(BanHandler), nameof(BanHandler.CheckExpiration)))) + offset; + + CodeInstruction addToUnbannedListInstruction = newInstructions[index]; + newInstructions.InsertRange(index, new[] + { + // id + new CodeInstruction(OpCodes.Ldloc, 4).MoveLabelsFrom(addToUnbannedListInstruction), + + // type + new(OpCodes.Ldarg_0), + + // true + new(OpCodes.Ldc_I4_1), + + // UnbanningEventArgs ev = new(string, BanHandler.BanType, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnbanningEventArgs))[0]), + + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Server.OnUnbanning(ev); + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnUnbanning))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(UnbanningEventArgs), nameof(UnbanningEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + + new(OpCodes.Ret), + }); + + // Add label to ldloc.1 + addToUnbannedListInstruction.WithLabels(continueLabel); + + for (int z = 0; z < newInstructions.Count; z++) + { yield return newInstructions[z]; + } ListPool.Pool.Return(newInstructions); }