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);
}