diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs index 2dd66766c..e85b54ed6 100644 --- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs +++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs @@ -435,12 +435,11 @@ public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwne /// /// EffectOnlySCP207. /// - /// MirrorExtensions.SendCustomSync(player, player.ReferenceHub.networkIdentity, typeof(PlayerEffectsController), (writer) => { - /// writer.WriteUInt64(1ul); // DirtyObjectsBit - /// writer.WriteUInt32(1); // DirtyIndexCount + /// MirrorExtensions.SendFakeSyncObject(player, player.NetworkIdentity, typeof(PlayerEffectsController), (writer) => { + /// writer.WriteULong(1ul); // DirtyObjectsBit + /// writer.WriteUInt(1); // DirtyIndexCount /// writer.WriteByte((byte)SyncList<byte>.Operation.OP_SET); // Operations - /// writer.WriteUInt32(17); // EditIndex - /// writer.WriteByte(1); // Value + /// writer.WriteUInt(17); // EditIndex /// }); /// /// diff --git a/EXILED/Exiled.API/Features/Camera.cs b/EXILED/Exiled.API/Features/Camera.cs index 67f5d7418..56e7e7f24 100644 --- a/EXILED/Exiled.API/Features/Camera.cs +++ b/EXILED/Exiled.API/Features/Camera.cs @@ -28,7 +28,7 @@ public class Camera : IWrapper, IWorldSpace /// /// A containing all known s and their corresponding . /// - internal static readonly Dictionary Camera079ToCamera = new(250); + internal static readonly Dictionary Camera079ToCamera = new(250, new ComponentsEqualityComparer()); private static readonly Dictionary NameToCameraType = new() { diff --git a/EXILED/Exiled.API/Features/Doors/AirlockController.cs b/EXILED/Exiled.API/Features/Doors/AirlockController.cs index 62645a392..eceda5f31 100644 --- a/EXILED/Exiled.API/Features/Doors/AirlockController.cs +++ b/EXILED/Exiled.API/Features/Doors/AirlockController.cs @@ -20,7 +20,7 @@ public class AirlockController /// /// A containing all known 's and their corresponding . /// - internal static readonly Dictionary BaseToExiledControllers = new(); + internal static readonly Dictionary BaseToExiledControllers = new(new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs index 584dc3fb0..230e05b0b 100644 --- a/EXILED/Exiled.API/Features/Doors/Door.cs +++ b/EXILED/Exiled.API/Features/Doors/Door.cs @@ -38,7 +38,7 @@ public class Door : TypeCastObject, IWrapper, IWorldSpace /// /// A containing all known 's and their corresponding . /// - internal static readonly Dictionary DoorVariantToDoor = new(); + internal static readonly Dictionary DoorVariantToDoor = new(new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Generator.cs b/EXILED/Exiled.API/Features/Generator.cs index b3f5bfb69..315f9af0f 100644 --- a/EXILED/Exiled.API/Features/Generator.cs +++ b/EXILED/Exiled.API/Features/Generator.cs @@ -26,7 +26,7 @@ public class Generator : IWrapper, IWorldSpace /// /// A of on the map. /// - internal static readonly Dictionary Scp079GeneratorToGenerator = new(); + internal static readonly Dictionary Scp079GeneratorToGenerator = new(new ComponentsEqualityComparer()); private Room room; /// diff --git a/EXILED/Exiled.API/Features/Hazards/Hazard.cs b/EXILED/Exiled.API/Features/Hazards/Hazard.cs index 6861d1a68..36695f6de 100644 --- a/EXILED/Exiled.API/Features/Hazards/Hazard.cs +++ b/EXILED/Exiled.API/Features/Hazards/Hazard.cs @@ -25,7 +25,7 @@ public class Hazard : TypeCastObject, IWrapper /// /// with to it's . /// - internal static readonly Dictionary EnvironmentalHazardToHazard = new(); + internal static readonly Dictionary EnvironmentalHazardToHazard = new(new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Items/Ammo.cs b/EXILED/Exiled.API/Features/Items/Ammo.cs index 4c88a7f84..64c598ad7 100644 --- a/EXILED/Exiled.API/Features/Items/Ammo.cs +++ b/EXILED/Exiled.API/Features/Items/Ammo.cs @@ -20,7 +20,7 @@ public class Ammo : Item, IWrapper /// /// Gets the absolute maximum amount of ammo that may be held at one time, if ammo is forcefully given to the player (regardless of worn armor or server configuration). /// - /// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see . + /// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see . /// /// public const ushort AmmoLimit = ushort.MaxValue; diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs index fd0bba35a..b38f4aa18 100644 --- a/EXILED/Exiled.API/Features/Items/Item.cs +++ b/EXILED/Exiled.API/Features/Items/Item.cs @@ -43,7 +43,7 @@ public class Item : TypeCastObject, IWrapper /// /// A dictionary of all 's that have been converted into . /// - internal static readonly Dictionary BaseToItem = new(); + internal static readonly Dictionary BaseToItem = new(new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Lift.cs b/EXILED/Exiled.API/Features/Lift.cs index 944e5bd77..5222ca985 100644 --- a/EXILED/Exiled.API/Features/Lift.cs +++ b/EXILED/Exiled.API/Features/Lift.cs @@ -33,7 +33,7 @@ public class Lift : IWrapper, IWorldSpace /// /// A containing all known s and their corresponding . /// - internal static readonly Dictionary ElevatorChamberToLift = new(8); + internal static readonly Dictionary ElevatorChamberToLift = new(8, new ComponentsEqualityComparer()); /// /// Internal list that contains all ElevatorDoor for current group. diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index b62524745..9172aab3e 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -80,6 +80,16 @@ public class Player : TypeCastObject, IEntity, IWorldSpace /// A list of the player's items. /// internal readonly List ItemsValue = new(8); + + /// + /// A dictionary of custom item category limits. + /// + internal Dictionary CustomCategoryLimits = new(); + + /// + /// A dictionary of custom ammo limits. + /// + internal Dictionary CustomAmmoLimits = new(); #pragma warning restore SA1401 private readonly HashSet componentsInChildren = new(); @@ -2382,21 +2392,170 @@ public bool DropAmmo(AmmoType ammoType, ushort amount, bool checkMinimals = fals /// /// Gets the maximum amount of ammo the player can hold, given the ammo . - /// This method factors in the armor the player is wearing, as well as server configuration. - /// For the maximum amount of ammo that can be given regardless of worn armor and server configuration, see . /// /// The of the ammo to check. - /// The maximum amount of ammo this player can carry. Guaranteed to be between 0 and . - public int GetAmmoLimit(AmmoType type) => - InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub); + /// If the method should ignore the armor the player is wearing. + /// The maximum amount of ammo this player can carry. + public ushort GetAmmoLimit(AmmoType type, bool ignoreArmor = false) + { + if (ignoreArmor) + { + if (CustomAmmoLimits.TryGetValue(type, out ushort limit)) + return limit; + + ItemType itemType = type.GetItemType(); + return ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FirstOrDefault(x => x.AmmoType == itemType).Limit; + } + + return InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub); + } + + /// + /// Gets the maximum amount of ammo the player can hold, given the ammo . + /// This limit will scale with the armor the player is wearing. + /// For armor ammo limits, see . + /// + /// The of the ammo to check. + /// The number that will define the new limit. + public void SetAmmoLimit(AmmoType ammoType, ushort limit) + { + CustomAmmoLimits[ammoType] = limit; + + ItemType itemType = ammoType.GetItemType(); + int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType); + MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer => + { + writer.WriteULong(2ul); + writer.WriteUInt(1); + writer.WriteByte((byte)SyncList.Operation.OP_SET); + writer.WriteInt(index); + writer.WriteAmmoLimit(new() { Limit = limit, AmmoType = itemType, }); + }); + } + + /// + /// Reset a custom limit. + /// + /// The of the ammo to reset. + public void ResetAmmoLimit(AmmoType ammoType) + { + if (!HasCustomAmmoLimit(ammoType)) + { + Log.Error($"{nameof(Player)}.{nameof(ResetAmmoLimit)}(AmmoType): AmmoType.{ammoType} does not have a custom limit."); + return; + } + + CustomAmmoLimits.Remove(ammoType); + + ItemType itemType = ammoType.GetItemType(); + int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType); + MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer => + { + writer.WriteULong(2ul); + writer.WriteUInt(1); + writer.WriteByte((byte)SyncList.Operation.OP_SET); + writer.WriteInt(index); + writer.WriteAmmoLimit(ServerConfigSynchronizer.Singleton.AmmoLimitsSync[index]); + }); + } + + /// + /// Check if the player has a custom limit for a specific . + /// + /// The to check. + /// If the player has a custom limit for the specific . + public bool HasCustomAmmoLimit(AmmoType ammoType) => CustomAmmoLimits.ContainsKey(ammoType); /// /// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration. /// /// The to check. + /// If the method should ignore the armor the player is wearing. /// The maximum amount of items in the category that the player can hold. - public int GetCategoryLimit(ItemCategory category) => - InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub); + public sbyte GetCategoryLimit(ItemCategory category, bool ignoreArmor = false) + { + int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category); + + if (ignoreArmor && index != -1) + { + if (CustomCategoryLimits.TryGetValue(category, out sbyte customLimit)) + return customLimit; + + return ServerConfigSynchronizer.Singleton.CategoryLimits[index]; + } + + sbyte limit = InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub); + + return limit == -1 ? (sbyte)1 : limit; + } + + /// + /// Set the maximum amount of an the player can hold. Only works with , , , and . + /// This limit will scale with the armor the player is wearing. + /// For armor category limits, see . + /// + /// The to check. + /// The number that will define the new limit. + public void SetCategoryLimit(ItemCategory category, sbyte limit) + { + int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category); + + if (index == -1) + { + Log.Error($"{nameof(Player)}.{nameof(SetCategoryLimit)}(ItemCategory, sbyte): Cannot set category limit for ItemCategory.{category}."); + return; + } + + CustomCategoryLimits[category] = limit; + + MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer => + { + writer.WriteULong(1ul); + writer.WriteUInt(1); + writer.WriteByte((byte)SyncList.Operation.OP_SET); + writer.WriteInt(index); + writer.WriteSByte(limit); + }); + } + + /// + /// Reset a custom limit. Only works with , , , and . + /// + /// The of the category to reset. + public void ResetCategoryLimit(ItemCategory category) + { + int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category); + + if (index == -1) + { + Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory, sbyte): Cannot reset category limit for ItemCategory.{category}."); + return; + } + + if (!HasCustomCategoryLimit(category)) + { + Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory): ItemCategory.{category} does not have a custom limit."); + return; + } + + CustomCategoryLimits.Remove(category); + + MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer => + { + writer.WriteULong(1ul); + writer.WriteUInt(1); + writer.WriteByte((byte)SyncList.Operation.OP_SET); + writer.WriteInt(index); + writer.WriteSByte(ServerConfigSynchronizer.Singleton.CategoryLimits[index]); + }); + } + + /// + /// Check if the player has a custom limit for a specific . + /// + /// The to check. + /// If the player has a custom limit for the specific . + public bool HasCustomCategoryLimit(ItemCategory category) => CustomCategoryLimits.ContainsKey(category); /// /// Adds an item of the specified type with default durability(ammo/charge) and no mods to the player's inventory. diff --git a/EXILED/Exiled.API/Features/Ragdoll.cs b/EXILED/Exiled.API/Features/Ragdoll.cs index 9d73104fe..e1aefa56a 100644 --- a/EXILED/Exiled.API/Features/Ragdoll.cs +++ b/EXILED/Exiled.API/Features/Ragdoll.cs @@ -40,7 +40,7 @@ public class Ragdoll : IWrapper, IWorldSpace /// /// A containing all known s and their corresponding . /// - internal static readonly Dictionary BasicRagdollToRagdoll = new(250); + internal static readonly Dictionary BasicRagdollToRagdoll = new(250, new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Recontainer.cs b/EXILED/Exiled.API/Features/Recontainer.cs index bdbbfcdf7..a63f8ada5 100644 --- a/EXILED/Exiled.API/Features/Recontainer.cs +++ b/EXILED/Exiled.API/Features/Recontainer.cs @@ -35,6 +35,11 @@ public static class Recontainer /// public static bool IsCassieBusy => Base.CassieBusy; + /// + /// Gets a value about how many generator have been activated. + /// + public static int EngagedGeneratorCount => Base._prevEngaged; + /// /// Gets or sets a value indicating whether the containment zone is open. /// diff --git a/EXILED/Exiled.API/Features/Roles/FpcRole.cs b/EXILED/Exiled.API/Features/Roles/FpcRole.cs index 27abeded8..dcf4e27cb 100644 --- a/EXILED/Exiled.API/Features/Roles/FpcRole.cs +++ b/EXILED/Exiled.API/Features/Roles/FpcRole.cs @@ -8,9 +8,11 @@ namespace Exiled.API.Features.Roles { using System.Collections.Generic; + using System.Reflection; using Exiled.API.Features.Pools; + using HarmonyLib; using PlayerRoles; using PlayerRoles.FirstPersonControl; @@ -24,6 +26,7 @@ namespace Exiled.API.Features.Roles /// public abstract class FpcRole : Role { + private static FieldInfo enableFallDamageField; private bool isUsingStamina = true; /// @@ -55,6 +58,19 @@ public RelativePosition RelativePosition set => FirstPersonController.FpcModule.Motor.ReceivedPosition = value; } + /// + /// Gets or sets a value indicating whether if the player should get damage. + /// + public bool IsFallDamageEnable + { + get => FirstPersonController.FpcModule.Motor._enableFallDamage; + set + { + enableFallDamageField ??= AccessTools.Field(typeof(FpcMotor), nameof(FpcMotor._enableFallDamage)); + enableFallDamageField.SetValue(FirstPersonController.FpcModule.Motor, value); + } + } + /// /// Gets or sets a value indicating whether if a rotation is detected on the player. /// diff --git a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs index aecc872d4..eff90bc44 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs @@ -25,7 +25,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-049. /// - public class Scp049Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole + public class Scp049Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs index 8782137a7..a3de5059c 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs @@ -16,6 +16,7 @@ namespace Exiled.API.Features.Roles using MapGeneration; using Mirror; using PlayerRoles; + using PlayerRoles.PlayableScps; using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.PlayableScps.Scp079.Cameras; using PlayerRoles.PlayableScps.Scp079.Pinging; @@ -31,7 +32,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-079. /// - public class Scp079Role : Role, ISubroutinedScpRole + public class Scp079Role : Role, ISubroutinedScpRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Roles/Scp096Role.cs b/EXILED/Exiled.API/Features/Roles/Scp096Role.cs index 6d2662ad3..f30342b17 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp096Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp096Role.cs @@ -11,6 +11,7 @@ namespace Exiled.API.Features.Roles using System.Linq; using PlayerRoles; + using PlayerRoles.PlayableScps; using PlayerRoles.PlayableScps.HumeShield; using PlayerRoles.PlayableScps.Scp096; using PlayerRoles.Subroutines; @@ -20,7 +21,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-096. /// - public class Scp096Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole + public class Scp096Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Roles/Scp106Role.cs b/EXILED/Exiled.API/Features/Roles/Scp106Role.cs index 826e756a5..9480a37bc 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp106Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp106Role.cs @@ -11,6 +11,7 @@ namespace Exiled.API.Features.Roles using Exiled.API.Enums; using PlayerRoles; + using PlayerRoles.PlayableScps; using PlayerRoles.PlayableScps.HumeShield; using PlayerRoles.PlayableScps.Scp049; using PlayerRoles.PlayableScps.Scp106; @@ -24,7 +25,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-106. /// - public class Scp106Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole + public class Scp106Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Roles/Scp173Role.cs b/EXILED/Exiled.API/Features/Roles/Scp173Role.cs index 23536f696..3a5f625ca 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp173Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp173Role.cs @@ -13,6 +13,7 @@ namespace Exiled.API.Features.Roles using Exiled.API.Features.Hazards; using Mirror; using PlayerRoles; + using PlayerRoles.PlayableScps; using PlayerRoles.PlayableScps.HumeShield; using PlayerRoles.PlayableScps.Scp173; using PlayerRoles.Subroutines; @@ -23,7 +24,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-173. /// - public class Scp173Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole + public class Scp173Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs b/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs index 3f7f6fae1..0d23e48ba 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs @@ -23,7 +23,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-3114. /// - public class Scp3114Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole + public class Scp3114Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs index b6dca72b8..ec404f349 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs @@ -13,6 +13,7 @@ namespace Exiled.API.Features.Roles using Exiled.API.Features.Pools; using PlayerRoles; + using PlayerRoles.PlayableScps; using PlayerRoles.PlayableScps.HumeShield; using PlayerRoles.PlayableScps.Scp939; using PlayerRoles.PlayableScps.Scp939.Mimicry; @@ -28,7 +29,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents SCP-939. /// - public class Scp939Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole + public class Scp939Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs index 8d0b70fe3..4ca8bc4d3 100644 --- a/EXILED/Exiled.API/Features/Room.cs +++ b/EXILED/Exiled.API/Features/Room.cs @@ -32,7 +32,7 @@ public class Room : MonoBehaviour, IWorldSpace /// /// A containing all known s and their corresponding . /// - internal static readonly Dictionary RoomIdentifierToRoom = new(250); + internal static readonly Dictionary RoomIdentifierToRoom = new(250, new ComponentsEqualityComparer()); /// /// Gets a of which contains all the instances. diff --git a/EXILED/Exiled.API/Features/TeslaGate.cs b/EXILED/Exiled.API/Features/TeslaGate.cs index 7ebdf6480..d161e6b08 100644 --- a/EXILED/Exiled.API/Features/TeslaGate.cs +++ b/EXILED/Exiled.API/Features/TeslaGate.cs @@ -27,7 +27,7 @@ public class TeslaGate : IWrapper, IWorldSpace /// /// A containing all known s and their corresponding . /// - internal static readonly Dictionary BaseTeslaGateToTeslaGate = new(10); + internal static readonly Dictionary BaseTeslaGateToTeslaGate = new(10, new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.API/Features/Window.cs b/EXILED/Exiled.API/Features/Window.cs index 961fbab88..63ca362fd 100644 --- a/EXILED/Exiled.API/Features/Window.cs +++ b/EXILED/Exiled.API/Features/Window.cs @@ -25,7 +25,7 @@ public class Window : IWrapper, IWorldSpace /// /// A containing all known s and their corresponding . /// - internal static readonly Dictionary BreakableWindowToWindow = new(); + internal static readonly Dictionary BreakableWindowToWindow = new(new ComponentsEqualityComparer()); /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs index ff3cd5762..28fd021f0 100644 --- a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs +++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs @@ -67,7 +67,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.IsLdarg(0)); newInstructions[lastIndex].labels.Add(doorSpawn); + // Replace + // "base.RegisterUnspawnedObject(doorNametagExtension.TargetDoor, itemPickupBase.gameObject);" + // with "base.RegisterUnspawnedObject(ev.Door.Base, itemPickupBase.gameObject);" offset = -1; index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldfld) + offset; @@ -122,7 +120,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - int e = 0; + LocalBuilder fpcRole = generator.DeclareLocal(typeof(FpcStandardRoleBase)); + + // replace HumanRole to FpcStandardRoleBase + newInstructions.Find(x => x.opcode == OpCodes.Isinst).operand = typeof(FpcStandardRoleBase); + + // after this index all invalid exit are considered Custom + int customExit = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldarg_0); for (int i = 0; i < newInstructions.Count; i++) { - CodeInstruction codeInstruction = newInstructions[i]; - if (codeInstruction.opcode == OpCodes.Ldc_I4_0) - { - e++; - if (e > 3) - { - newInstructions[i].opcode = OpCodes.Ldc_I4_5; - } - } + OpCode opcode = newInstructions[i].opcode; + if (opcode == OpCodes.Stloc_0) + newInstructions[i] = new(OpCodes.Stloc_S, fpcRole.LocalIndex); + else if (opcode == OpCodes.Ldloc_0) + newInstructions[i] = new(OpCodes.Ldloc_S, fpcRole.LocalIndex); + else if (opcode == OpCodes.Ldc_I4_0 && i > customExit) + newInstructions[i].opcode = OpCodes.Ldc_I4_5; } for (int z = 0; z < newInstructions.Count; z++) diff --git a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs index bba54c92e..5d22e96c5 100644 --- a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs +++ b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs @@ -40,8 +40,8 @@ private static IEnumerable Transpiler(IEnumerable instruction.opcode == OpCodes.Newarr) + offset; + int offset = 2; + int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldarg_S && instruction.operand == (object)4) + offset; Label ret = generator.DefineLabel(); diff --git a/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs b/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs new file mode 100644 index 000000000..eb533862e --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs @@ -0,0 +1,35 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using System; + + using Exiled.API.Extensions; + using Exiled.API.Features; + using HarmonyLib; + using InventorySystem.Configs; + using UnityEngine; + + /// + /// Patches the delegate. + /// Sync . + /// Changes to . + /// + [HarmonyPatch(typeof(InventoryLimits), nameof(InventoryLimits.GetAmmoLimit), new Type[] { typeof(ItemType), typeof(ReferenceHub) })] + internal static class GetCustomAmmoLimit + { +#pragma warning disable SA1313 + private static void Postfix(ItemType ammoType, ReferenceHub player, ref ushort __result) + { + if (!Player.TryGet(player, out Player ply) || !ply.CustomAmmoLimits.TryGetValue(ammoType.GetAmmoType(), out ushort limit)) + return; + + __result = (ushort)Mathf.Clamp(limit + __result - InventoryLimits.GetAmmoLimit(null, ammoType), ushort.MinValue, ushort.MaxValue); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs b/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs new file mode 100644 index 000000000..8ceff7538 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs @@ -0,0 +1,34 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using System; + + using Exiled.API.Features; + using HarmonyLib; + using InventorySystem.Configs; + using UnityEngine; + + /// + /// Patches the delegate. + /// Sync , . + /// Changes to . + /// + [HarmonyPatch(typeof(InventoryLimits), nameof(InventoryLimits.GetCategoryLimit), new Type[] { typeof(ItemCategory), typeof(ReferenceHub), })] + internal static class GetCustomCategoryLimit + { +#pragma warning disable SA1313 + private static void Postfix(ItemCategory category, ReferenceHub player, ref sbyte __result) + { + if (!Player.TryGet(player, out Player ply) || !ply.CustomCategoryLimits.TryGetValue(category, out sbyte limit)) + return; + + __result = (sbyte)Mathf.Clamp(limit + __result - InventoryLimits.GetCategoryLimit(null, category), sbyte.MinValue, sbyte.MaxValue); + } + } +}