diff --git a/EXILED/EXILED.props b/EXILED/EXILED.props index c9b421e84..7d4a42673 100644 --- a/EXILED/EXILED.props +++ b/EXILED/EXILED.props @@ -15,7 +15,7 @@ - 9.0.0 + 9.4.0 false diff --git a/EXILED/Exiled.API/Enums/DamageType.cs b/EXILED/Exiled.API/Enums/DamageType.cs index add8d86ca..602ad2f74 100644 --- a/EXILED/Exiled.API/Enums/DamageType.cs +++ b/EXILED/Exiled.API/Enums/DamageType.cs @@ -254,5 +254,20 @@ public enum DamageType /// Damage caused by the marshmallow man. /// Marshmallow, + + /// + /// Damage caused by , or . + /// + Scp1507, + + /// + /// Damage caused by Scp956 the pinata. + /// + Scp956, + + /// + /// Damage caused by . + /// + SnowBall, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/DoorType.cs b/EXILED/Exiled.API/Enums/DoorType.cs index 36bb9b853..c3e022172 100644 --- a/EXILED/Exiled.API/Enums/DoorType.cs +++ b/EXILED/Exiled.API/Enums/DoorType.cs @@ -7,6 +7,8 @@ namespace Exiled.API.Enums { + using System; + using Exiled.API.Features.Doors; using Interactables.Interobjects; @@ -200,6 +202,7 @@ public enum DoorType /// /// Represents the NUKE_ARMORY door. /// + [Obsolete("This Door has been removed from the game.")] NukeArmory, /// diff --git a/EXILED/Exiled.API/Enums/EffectType.cs b/EXILED/Exiled.API/Enums/EffectType.cs index b2894486e..eb29bd586 100644 --- a/EXILED/Exiled.API/Enums/EffectType.cs +++ b/EXILED/Exiled.API/Enums/EffectType.cs @@ -259,5 +259,29 @@ public enum EffectType /// . /// Blurred, + + /// + /// Makes you a flamingo. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + BecomingFlamingo, + + /// + /// Makes you a Child after eating Cake. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + Scp559, + + /// + /// Scp956 found you. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + Scp956Target, + + /// + /// you are snowed. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + Snowed, } } 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/Enums/ObjectiveType.cs b/EXILED/Exiled.API/Enums/ObjectiveType.cs new file mode 100644 index 000000000..3979ac266 --- /dev/null +++ b/EXILED/Exiled.API/Enums/ObjectiveType.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Enums +{ + /// + /// An enum representing the different types of objectives. + /// + /// + public enum ObjectiveType + { + /// + /// Unknown objective. + /// + None, + + /// + /// Objective that is completed when SCP item is picked up. + /// + ScpItemPickup, + + /// + /// Objective that is completed when enemy military is damaged. + /// + HumanDamage, + + /// + /// Objective that is completed when enemy military is killed. + /// + HumanKill, + + /// + /// Objective that is completed when generator is activated. + /// + GeneratorActivation, + + /// + /// Objective that is completed when player escapes. + /// + Escape, + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/Scp939VisibilityStates.cs b/EXILED/Exiled.API/Enums/Scp939VisibilityStates.cs new file mode 100644 index 000000000..d9be0eadc --- /dev/null +++ b/EXILED/Exiled.API/Enums/Scp939VisibilityStates.cs @@ -0,0 +1,47 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Enums +{ + using Features.Roles; + + /// + /// Unique identifier for a . + /// + public enum Scp939VisibilityState + { + /// + /// SCP-939 doesnt see an other player, by default FPC role logic. + /// + None, + + /// + /// SCP-939 doesnt see an player, by basic SCP-939 logic. + /// + NotSeen, + + /// + /// SCP-939 sees an other player, who is teammate SCP. + /// + SeenAsScp, + + /// + /// SCP-939 sees an other player due the Alpha Warhead detonation. + /// + SeenByDetonation, + + /// + /// SCP-939 sees an other player, due the base-game vision range logic. + /// + SeenByRange, + + /// + /// SCP-939 sees an other player for a while, after it's out of range. + /// + SeenByLastTime, + } +} diff --git a/EXILED/Exiled.API/Enums/SpawnLocationType.cs b/EXILED/Exiled.API/Enums/SpawnLocationType.cs index 2a099f224..1ebd57cc9 100644 --- a/EXILED/Exiled.API/Enums/SpawnLocationType.cs +++ b/EXILED/Exiled.API/Enums/SpawnLocationType.cs @@ -81,6 +81,7 @@ public enum SpawnLocationType /// /// Inside the Nuke armory. /// + [Obsolete("This Location has been removed from the game.")] InsideNukeArmory, /// diff --git a/EXILED/Exiled.API/Enums/SpawnReason.cs b/EXILED/Exiled.API/Enums/SpawnReason.cs index 8bf27047c..8274b9933 100644 --- a/EXILED/Exiled.API/Enums/SpawnReason.cs +++ b/EXILED/Exiled.API/Enums/SpawnReason.cs @@ -56,5 +56,10 @@ public enum SpawnReason : byte // TOTO: Remove this file and use Basegame /// The user will be destroyed. /// Destroyed, + + /// + /// The user has been spawn by the usage of an Item. + /// + ItemUsage, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Exiled.API.csproj b/EXILED/Exiled.API/Exiled.API.csproj index 3d6605c62..961b44e98 100644 --- a/EXILED/Exiled.API/Exiled.API.csproj +++ b/EXILED/Exiled.API/Exiled.API.csproj @@ -18,7 +18,6 @@ - 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/DamageTypeExtensions.cs b/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs index bb2a246f1..7e496e77a 100644 --- a/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs +++ b/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs @@ -12,6 +12,7 @@ namespace Exiled.API.Extensions using Enums; using Features; + using PlayerRoles.PlayableScps.Scp1507; using PlayerRoles.PlayableScps.Scp3114; using PlayerStatsSystem; @@ -180,6 +181,12 @@ public static DamageType GetDamageType(DamageHandlerBase damageHandlerBase) return DamageType.MicroHid; case DisruptorDamageHandler: return DamageType.ParticleDisruptor; + case Scp1507DamageHandler: + return DamageType.Scp1507; + case Scp956DamageHandler: + return DamageType.Scp956; + case SnowballDamageHandler: + return DamageType.SnowBall; case Scp049DamageHandler scp049DamageHandler: return scp049DamageHandler.DamageSubType switch { diff --git a/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs b/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs index 0b977356e..ad8119fe3 100644 --- a/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs +++ b/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs @@ -67,9 +67,6 @@ public static class EffectTypeExtension { EffectType.AntiScp207, typeof(AntiScp207) }, { EffectType.Scanned, typeof(Scanned) }, { EffectType.SilentWalk, typeof(SilentWalk) }, -#pragma warning disable CS0618 - { EffectType.Marshmallow, typeof(MarshmallowEffect) }, -#pragma warning restore CS0618 { EffectType.Strangled, typeof(Strangled) }, { EffectType.Ghostly, typeof(Ghostly) }, { EffectType.FogControl, typeof(FogControl) }, @@ -78,6 +75,13 @@ public static class EffectTypeExtension { EffectType.SeveredEyes, typeof(SeveredEyes) }, { EffectType.PitDeath, typeof(PitDeath) }, { EffectType.Blurred, typeof(Blurred) }, + #pragma warning disable CS0618 + { EffectType.Marshmallow, typeof(MarshmallowEffect) }, + { EffectType.BecomingFlamingo, typeof(BecomingFlamingo) }, + { EffectType.Scp559, typeof(Scp559Effect) }, + { EffectType.Scp956Target, typeof(Scp956Target) }, + { EffectType.Snowed, typeof(Snowed) }, + #pragma warning restore CS0618 }); /// 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/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs index 1e8187c09..cf0a97872 100644 --- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs +++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs @@ -24,6 +24,7 @@ namespace Exiled.API.Extensions using PlayerRoles; using PlayerRoles.FirstPersonControl; using PlayerRoles.PlayableScps.Scp049.Zombies; + using PlayerRoles.PlayableScps.Scp1507; using PlayerRoles.Voice; using RelativePositioning; @@ -269,6 +270,14 @@ public static void ChangeAppearance(this Player player, RoleTypeId type, IEnumer writer.WriteBool(true); } + if (roleBase is Scp1507Role) + { + if (player.Role.Base is not Scp1507Role) + isRisky = true; + + writer.WriteByte((byte)player.Role.SpawnReason); + } + if (roleBase is FpcStandardRoleBase fpc) { if (player.Role.Base is not FpcStandardRoleBase playerfpc) diff --git a/EXILED/Exiled.API/Extensions/SpawnExtensions.cs b/EXILED/Exiled.API/Extensions/SpawnExtensions.cs index b7d35f404..a987a5626 100644 --- a/EXILED/Exiled.API/Extensions/SpawnExtensions.cs +++ b/EXILED/Exiled.API/Extensions/SpawnExtensions.cs @@ -97,7 +97,6 @@ public static Vector3 GetPosition(this SpawnLocationType location) SpawnLocationType.Inside173Bottom => "173_BOTTOM", SpawnLocationType.InsideLczArmory => "LCZ_ARMORY", SpawnLocationType.InsideHczArmory => "HCZ_ARMORY", - SpawnLocationType.InsideNukeArmory => "NUKE_ARMORY", SpawnLocationType.InsideSurfaceNuke => "SURFACE_NUKE", SpawnLocationType.Inside079Secondary => "079_SECOND", SpawnLocationType.Inside173Connector => "173_CONNECTOR", diff --git a/EXILED/Exiled.API/Features/Coffee.cs b/EXILED/Exiled.API/Features/Coffee.cs new file mode 100644 index 000000000..4133be430 --- /dev/null +++ b/EXILED/Exiled.API/Features/Coffee.cs @@ -0,0 +1,120 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Extensions; + using Exiled.API.Interfaces; + using UnityEngine; + + using BaseCoffee = global::Coffee; + + /// + /// A wrapper for coffee cup. + /// + public class Coffee : IWrapper + { + /// + /// Gets the containing to . + /// + internal static readonly Dictionary BaseToWrapper = new(); + + /// + /// Initializes a new instance of the class. + /// + /// + public Coffee(BaseCoffee coffee) + { + Base = coffee; + + BaseToWrapper.Add(coffee, this); + } + + /// + /// Gets or sets an of players who cannot interact with coffee cups. + /// + public static IEnumerable BlacklistedPlayers + { + get => BaseCoffee.BlacklistedPlayers.Select(Player.Get); + set + { + BaseCoffee.BlacklistedPlayers.Clear(); + + foreach (Player player in value) + BaseCoffee.BlacklistedPlayers.Add(player.ReferenceHub); + } + } + + /// + /// Gets the list with all available instanses. + /// + [Obsolete("This list will be empty")] + public static IReadOnlyCollection List => BaseToWrapper.Values; + + /// + /// Gets the base coffee instance. + /// + public BaseCoffee Base { get; } + + /// + /// Gets or sets a value indicating whether or not coffee has been drunk. + /// + public bool IsConsumed + { + get => Base.IsConsumed; + set => Base.NetworkIsConsumed = value; + } + + /// + /// Gets or sets text which will be displayed to player when he drinks coffee. + /// + public CoffeeTranslation CoffeeTranslation + { + get => Base._drinkText; + set => Base._drinkText = value; + } + + /// + /// Gets or sets the author of current . + /// + public string TranslationAuthor + { + get => Base._author; + set => Base._author = value; + } + + /// + /// Gets the color of a drink in a cup. + /// + public Color DrinkColor => Base._drinkColor; + + /// + /// Gets a given a instance. + /// + /// The instance. + /// The instance. + public static Coffee Get(BaseCoffee baseCoffee) => BaseToWrapper.TryGetValue(baseCoffee, out Coffee coffee) ? coffee : new(baseCoffee); + + /// + /// Gets a of matching the condition. + /// + /// The condition to satisfy. + /// A of matching the condition. + [Obsolete("The respond list will be empty")] + public static IEnumerable Get(Func predicate) => List.Where(predicate); + + /// + /// Interacts with . + /// + /// The player who interacts. If , it will be chosen randomly. + public void Interact(Player player = null) => Base.ServerInteract((player ?? Player.Get(x => x.IsHuman).GetRandomValue()).ReferenceHub, byte.MaxValue); + } +} 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/DamageHandlers/DamageHandlerBase.cs b/EXILED/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs index 4446475b8..e88eb3c02 100644 --- a/EXILED/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs +++ b/EXILED/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs @@ -15,6 +15,7 @@ namespace Exiled.API.Features.DamageHandlers using Enums; using Extensions; + using PlayerRoles.PlayableScps.Scp1507; using PlayerRoles.PlayableScps.Scp3114; using PlayerRoles.PlayableScps.Scp939; @@ -116,6 +117,12 @@ public virtual DamageType Type return DamageType.Scp939; case JailbirdDamageHandler: return DamageType.Jailbird; + case Scp1507DamageHandler: + return DamageType.Scp1507; + case Scp956DamageHandler: + return DamageType.Scp956; + case SnowballDamageHandler: + return DamageType.SnowBall; case Scp3114DamageHandler scp3114DamageHandler: return scp3114DamageHandler.Subtype switch { diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs index 63b4f62ce..fb947039d 100644 --- a/EXILED/Exiled.API/Features/Doors/Door.cs +++ b/EXILED/Exiled.API/Features/Doors/Door.cs @@ -627,14 +627,19 @@ 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, "ESCAPE_PRIMARY" => DoorType.EscapePrimary, "ESCAPE_SECONDARY" => DoorType.EscapeSecondary, "INTERCOM" => DoorType.Intercom, - "NUKE_ARMORY" => DoorType.NukeArmory, "LCZ_ARMORY" => DoorType.LczArmory, "SURFACE_NUKE" => DoorType.NukeSurface, "HCZ_ARMORY" => DoorType.HczArmory, diff --git a/EXILED/Exiled.API/Features/Items/Firearm.cs b/EXILED/Exiled.API/Features/Items/Firearm.cs index 9be796772..663144ff8 100644 --- a/EXILED/Exiled.API/Features/Items/Firearm.cs +++ b/EXILED/Exiled.API/Features/Items/Firearm.cs @@ -73,6 +73,11 @@ public Firearm(BaseFirearm itemBase) { BarrelMagazine ??= (BarrelMagazine)Magazine.Get(ammoModule); } + + if (module is HitscanHitregModuleBase hitregModule) + { + HitscanHitregModule = hitregModule; + } } } @@ -136,6 +141,11 @@ public static IReadOnlyDictionary public BarrelMagazine BarrelMagazine { get; } + /// + /// Gets a primaty magazine for current firearm. + /// + public HitscanHitregModuleBase HitscanHitregModule { get; } + /// /// Gets or sets the amount of ammo in the firearm magazine. /// @@ -176,6 +186,57 @@ public int MaxMagazineAmmo set => PrimaryMagazine.MaxAmmo = value; } + /// + /// Gets or sets the damage for this firearm. + /// + public float Damage + { + get => HitscanHitregModule.BaseDamage; + set => HitscanHitregModule.BaseDamage = value; + } + + /// + /// Gets or sets the inaccuracy for this firearm. + /// + public float Inaccuracy + { + get => HitscanHitregModule.BaseBulletInaccuracy; + set => HitscanHitregModule.BaseBulletInaccuracy = value; + } + + /// + /// Gets or sets the penetration for this firearm. + /// + public float Penetration + { + get => HitscanHitregModule.BasePenetration; + set => HitscanHitregModule.BasePenetration = value; + } + + /// + /// Gets or sets how much fast the value drop over the distance. + /// + public float DamageFalloffDistance + { + get => HitscanHitregModule.DamageFalloffDistance; + set => HitscanHitregModule.DamageFalloffDistance = value; + } + + /// + /// Gets the damage for this firearm with attachement modifier. + /// + public float EffectiveDamage => HitscanHitregModule.EffectiveDamage; + + /// + /// Gets the inaccuracy for this firearm with attachement modifier. + /// + public float EffectiveInaccuracy => HitscanHitregModule.CurrentInaccuracy; + + /// + /// Gets the penetration for this firearm with attachement modifier. + /// + public float EffectivePenetration => HitscanHitregModule.DisplayPenetration; + /// /// Gets or sets the amount of max ammo in the firearm barrel. /// @@ -225,7 +286,7 @@ public int MaxBarrelAmmo /// /// Gets a value indicating whether the firearm is being aimed. /// - public bool Aiming => Base.TryGetModule(out LinearAdsModule module) && module.AdsTarget; + public bool Aiming => Base.TryGetModule(out IAdsModule module) && module.AdsTarget; /// /// Gets a value indicating whether the firearm's flashlight module is enabled. diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs index 7e46a408f..710a031a1 100644 --- a/EXILED/Exiled.API/Features/Items/Item.cs +++ b/EXILED/Exiled.API/Features/Items/Item.cs @@ -357,7 +357,7 @@ public static T Get(ushort serial) /// The who owns the item by default. /// The specified type. /// The created. This can be cast as a subclass. - public static Item Create(ItemType type, Player owner = null) + public static Item Create(ItemType type, Player owner = null) // TODO modify return type to "T" where T : Item => Create(type, owner) as T; /// diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs index 65c858b74..b58f6b8dd 100644 --- a/EXILED/Exiled.API/Features/Items/Usable.cs +++ b/EXILED/Exiled.API/Features/Items/Usable.cs @@ -7,6 +7,7 @@ namespace Exiled.API.Features.Items { + using Exiled.API.Extensions; using Exiled.API.Features.Pickups; using Exiled.API.Interfaces; @@ -126,13 +127,23 @@ public override Pickup CreatePickup(Vector3 position, Quaternion? rotation = nul /// /// Uses the item. /// - /// The of the item cannot be . - public virtual void Use() + public virtual void Use() => Use(Owner); + + /// + /// Uses the item. + /// + /// Target to use an . + public virtual void Use(Player owner = null) { - if (Owner is null) - throw new System.InvalidOperationException("The Owner of the item cannot be null."); + Player oldOwner = Owner; + owner ??= Owner; + + Base.Owner = owner.ReferenceHub; + Base.ServerOnUsingCompleted(); + + typeof(UsableItemsController).InvokeStaticEvent(nameof(UsableItemsController.ServerOnUsingCompleted), new object[] { owner.ReferenceHub, Base }); - Owner.UseItem(this); + Base.Owner = oldOwner.ReferenceHub; } /// diff --git a/EXILED/Exiled.API/Features/Lift.cs b/EXILED/Exiled.API/Features/Lift.cs index ec2a87c84..43d83f09a 100644 --- a/EXILED/Exiled.API/Features/Lift.cs +++ b/EXILED/Exiled.API/Features/Lift.cs @@ -48,7 +48,7 @@ internal Lift(ElevatorChamber elevator) Base = elevator; ElevatorChamberToLift.Add(elevator, this); - internalDoorsList.AddRange(Interactables.Interobjects.ElevatorDoor.AllElevatorDoors[Group]); + internalDoorsList.AddRange(Elevator.AllElevatorDoors[Group]); } /// diff --git a/EXILED/Exiled.API/Features/Lockers/Locker.cs b/EXILED/Exiled.API/Features/Lockers/Locker.cs index 739015590..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); /// @@ -172,7 +179,7 @@ public void AddItem(Pickup item) Chamber chamber = Chambers.GetRandomValue(); // Determine the parent transform where the item will be placed. - Transform parentTransform = chamber.UseMultipleSpawnpoints && chamber.Spawnpoints.Count() > 0 + Transform parentTransform = chamber.UseMultipleSpawnpoints && chamber.Spawnpoints.Any() ? chamber.Spawnpoints.GetRandomValue() : chamber.Spawnpoint; @@ -180,13 +187,15 @@ public void AddItem(Pickup item) if (chamber.IsOpen) { item.Transform.SetParent(parentTransform); - item.Spawn(); + + if(!item.IsSpawned) + item.Spawn(); } else { // If the item is already spawned on the network, unspawn it before proceeding. - if (NetworkServer.spawned.ContainsKey(item.Base.netId)) - NetworkServer.UnSpawn(item.GameObject); + if (item.IsSpawned) + item.UnSpawn(); // Set the item's parent transform. item.Transform.SetParent(parentTransform); diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs index f6ffb6efe..183b94710 100644 --- a/EXILED/Exiled.API/Features/Map.cs +++ b/EXILED/Exiled.API/Features/Map.cs @@ -375,6 +375,12 @@ internal static void ClearCache() Firearm.ItemTypeToFirearmInstance.Clear(); Firearm.BaseCodesValue.Clear(); Firearm.AvailableAttachmentsValue.Clear(); + +#pragma warning disable CS0618 + Scp559.CakeToWrapper.Clear(); +#pragma warning restore CS0618 + + Coffee.BaseToWrapper.Clear(); } } } diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs index 4055dfa6a..e058efd07 100644 --- a/EXILED/Exiled.API/Features/Npc.cs +++ b/EXILED/Exiled.API/Features/Npc.cs @@ -276,7 +276,7 @@ public static Npc Spawn(string name, RoleTypeId role = RoleTypeId.None, bool ign Timing.CallDelayed(0.5f, () => { - npc.Role.Set(role, SpawnReason.RoundStart, position is null ? RoleSpawnFlags.All : RoleSpawnFlags.AssignInventory); + npc.Role.Set(role, SpawnReason.ForceClass, position is null ? RoleSpawnFlags.All : RoleSpawnFlags.AssignInventory); if (position is not null) npc.Position = position.Value; diff --git a/EXILED/Exiled.API/Features/Objectives/EscapeObjective.cs b/EXILED/Exiled.API/Features/Objectives/EscapeObjective.cs new file mode 100644 index 000000000..9b141f22f --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/EscapeObjective.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using Exiled.API.Enums; + using Exiled.API.Interfaces; + using PlayerRoles; + using Respawning.Objectives; + + using BaseObjective = Respawning.Objectives.EscapeObjective; + + /// + /// Represents an objective that is completed when a player escapes. + /// + public class EscapeObjective : HumanObjective, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// A instance. + internal EscapeObjective(BaseObjective objectiveFootprintBase) + : base(objectiveFootprintBase) + { + Base = objectiveFootprintBase; + } + + /// + public new BaseObjective Base { get; } + + /// + public override ObjectiveType Type { get; } = ObjectiveType.Escape; + + /// + /// Fakes player's escape and tries to achieve this objective. + /// + /// Target that has escaped. + /// Role that target will get after escaping. + public void Escape(Player player, RoleTypeId newRole) => Base.OnServerRoleSet(player.ReferenceHub, newRole, RoleChangeReason.Escaped); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Objectives/GeneratorActivatedObjective.cs b/EXILED/Exiled.API/Features/Objectives/GeneratorActivatedObjective.cs new file mode 100644 index 000000000..77e95fa5e --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/GeneratorActivatedObjective.cs @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using Exiled.API.Enums; + using Exiled.API.Interfaces; + using Respawning.Objectives; + + using BaseObjective = Respawning.Objectives.GeneratorActivatedObjective; + + /// + /// Represents an objective that is completed when a generator is activated. + /// + public class GeneratorActivatedObjective : HumanObjective, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// A instance. + internal GeneratorActivatedObjective(BaseObjective objectiveFootprintBase) + : base(objectiveFootprintBase) + { + Base = objectiveFootprintBase; + } + + /// + public new BaseObjective Base { get; } + + /// + public override ObjectiveType Type { get; } = ObjectiveType.GeneratorActivation; + + /// + /// Fakes generator activation and tries to achieve this objective. + /// + /// Generator that is activated. + /// Player that activated the generator. + public void Activate(Generator generator, Player player = null) => Base.OnGeneratorEngaged(generator.Base, (player ?? Server.Host).Footprint); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Objectives/HumanDamageObjective.cs b/EXILED/Exiled.API/Features/Objectives/HumanDamageObjective.cs new file mode 100644 index 000000000..5a9f136ba --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/HumanDamageObjective.cs @@ -0,0 +1,54 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using Exiled.API.Enums; + using Exiled.API.Features.DamageHandlers; + using Exiled.API.Interfaces; + using Respawning.Objectives; + + using BaseObjective = Respawning.Objectives.HumanDamageObjective; + + /// + /// A wrapper for the human damage objective. + /// + public class HumanDamageObjective : HumanObjective, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// + internal HumanDamageObjective(BaseObjective objectiveFootprintBase) + : base(objectiveFootprintBase) + { + Base = objectiveFootprintBase; + } + + /// + public new BaseObjective Base { get; } + + /// + public override ObjectiveType Type { get; } = ObjectiveType.HumanDamage; + + /// + /// Fakes player's damage and tries to achieve this objective. + /// + /// Attacker. + /// Target to damage. + /// Amount of damage. + /// Type of damage. + public void Damage(Player attacker, Player target, float amount, DamageType type = DamageType.Unknown) + => Damage(new CustomDamageHandler(target, attacker, amount, type, string.Empty)); + + /// + /// Fakes player's damage and tries to achieve this objective. + /// + /// An instance. + public void Damage(AttackerDamageHandler damageHandler) => Base.OnPlayerDamaged(damageHandler.Attacker.ReferenceHub, damageHandler.Base); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Objectives/HumanKillObjective.cs b/EXILED/Exiled.API/Features/Objectives/HumanKillObjective.cs new file mode 100644 index 000000000..7856d1845 --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/HumanKillObjective.cs @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using Exiled.API.Enums; + using Exiled.API.Features.DamageHandlers; + using Exiled.API.Interfaces; + using PlayerRoles; + using Respawning.Objectives; + + using BaseObjective = Respawning.Objectives.HumanKillObjective; + + /// + /// A wrapper for Human kill objective. + /// + public class HumanKillObjective : HumanObjective, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// + internal HumanKillObjective(BaseObjective objectiveFootprintBase) + : base(objectiveFootprintBase) + { + Base = objectiveFootprintBase; + } + + /// + public new BaseObjective Base { get; } + + /// + public override ObjectiveType Type { get; } = ObjectiveType.HumanKill; + + /// + /// Checks if the role is an enemy role. + /// + /// Target role. + /// Attacker. + /// true if role is an enemy role, false otherwise. + public bool IsValidEnemy(RoleTypeId target, Player player) => Base.IsValidEnemy(target, player.ReferenceHub); + + /// + /// Checks if the player is an enemy. + /// + /// Target player. + /// Attacker. + /// true if player is an enemy, false otherwise. + public bool IsValidEnemy(Player target, Player player) => IsValidEnemy(target.Role, player); + + /// + /// Fakes player's kill and tries to achieve this objective. + /// + /// An instance. + public void Kill(AttackerDamageHandler damageHandler) => Base.OnKill(damageHandler.Target.ReferenceHub, damageHandler.Base); + + /// + /// Fakes player's kill and tries to achieve this objective. + /// + /// Target player. + /// Attacker. + /// Damage type. + public void Kill(Player target, Player attacker, DamageType damageType = DamageType.Unknown) => Kill(new CustomDamageHandler(target, attacker, -1, damageType)); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Objectives/HumanObjective.cs b/EXILED/Exiled.API/Features/Objectives/HumanObjective.cs new file mode 100644 index 000000000..77dd37f5c --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/HumanObjective.cs @@ -0,0 +1,104 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using Exiled.API.Interfaces; + using Respawning.Objectives; + + /// + /// Represents a human objective. + /// + /// An objective footprint type. + public class HumanObjective : Objective, IWrapper> + where T : ObjectiveFootprintBase, new() + { + /// + /// Initializes a new instance of the class. + /// + /// A instance. + internal HumanObjective(HumanObjectiveBase objectiveFootprintBase) + : base(objectiveFootprintBase) + { + Base = objectiveFootprintBase; + } + + /// + public new HumanObjectiveBase Base { get; } + + /// + /// Gets or sets the objective footprint. + /// + /// Can be null. It's being set by game only before achieving. + public T ObjectiveFootprint + { + get => (T)Base.ObjectiveFootprint; + set => Base.ObjectiveFootprint = value; + } + + /// + /// Gets or sets the time reward. + /// + /// + /// Can be 0 if is null. + /// Setter affects only client notification. + /// + public float TimeReward + { + get => ObjectiveFootprint?.TimeReward ?? 0; + set + { + ObjectiveFootprint ??= new T(); + ObjectiveFootprint.TimeReward = value; + } + } + + /// + /// Gets or sets the influence reward. + /// + /// + /// Can be 0 if is null. + /// Setter affects only client notification. + /// + public float InfluenceReward + { + get => ObjectiveFootprint?.InfluenceReward ?? 0; + set + { + ObjectiveFootprint ??= new T(); + ObjectiveFootprint.InfluenceReward = value; + } + } + + /// + /// Gets or sets the achiever. + /// + /// + /// Can be null if is null. + /// Setter affects only client notification. + /// + public Player Achiever + { + get => ObjectiveFootprint == null ? null : Player.Get(ObjectiveFootprint.AchievingPlayer.Nickname); + set + { + ObjectiveFootprint ??= new T(); + ObjectiveFootprint.AchievingPlayer = new(value.Footprint); + } + } + + /// + /// Achieves the objective. + /// + /// An objective footprint instance. + public void Achieve(T objectiveFootprint) + { + ObjectiveFootprint = objectiveFootprint; + Achieve(); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Objectives/Objective.cs b/EXILED/Exiled.API/Features/Objectives/Objective.cs new file mode 100644 index 000000000..7497737b0 --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/Objective.cs @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Enums; + using Exiled.API.Features.Core; + using Exiled.API.Interfaces; + using PlayerRoles; + using Respawning; + using Respawning.Objectives; + + using BaseEscapeObjective = Respawning.Objectives.EscapeObjective; + using BaseGeneratorObjective = Respawning.Objectives.GeneratorActivatedObjective; + using BaseHumanDamageObjective = Respawning.Objectives.HumanDamageObjective; + using BaseHumanKillObjective = Respawning.Objectives.HumanKillObjective; + using BaseScpPickupObjective = Respawning.Objectives.ScpItemPickupObjective; + + /// + /// A wrapper for Faction objective. + /// + public class Objective : TypeCastObject, IWrapper + { + /// + /// A dictionary of all objectives. + /// + private static readonly Dictionary Objectives = new(); + + /// + /// Initializes a new instance of the class. + /// + /// + internal Objective(FactionObjectiveBase objectiveFootprintBase) + { + Base = objectiveFootprintBase; + Objectives.Add(Base, this); + } + + /// + /// Gets all objectives. + /// + public static IReadOnlyCollection List => Objectives.Values; + + /// + public FactionObjectiveBase Base { get; } + + /// + /// Gets the type of objective. + /// + public virtual ObjectiveType Type { get; } = ObjectiveType.None; + + /// + /// Gets the objective by its type. + /// + /// Type of objective. + /// An instance if found, null otherwise. + public static Objective Get(ObjectiveType type) => Get(type switch + { + ObjectiveType.ScpItemPickup => FactionInfluenceManager.Objectives.OfType().First(), + ObjectiveType.GeneratorActivation => FactionInfluenceManager.Objectives.OfType().First(), + ObjectiveType.HumanDamage => FactionInfluenceManager.Objectives.OfType().First(), + ObjectiveType.HumanKill => FactionInfluenceManager.Objectives.OfType().First(), + ObjectiveType.Escape => FactionInfluenceManager.Objectives.OfType().First(), + _ => null + }); + + /// + /// Gets the objective by its base. + /// + /// A instance. + /// An instance. + public static Objective Get(FactionObjectiveBase factionObjectiveBase) + { + if (Objectives.TryGetValue(factionObjectiveBase, out Objective objective)) + return objective; + + return factionObjectiveBase switch + { + BaseScpPickupObjective scpItemPickupObjective => new ScpItemPickupObjective(scpItemPickupObjective), + BaseGeneratorObjective generatorActivatedObjective => new GeneratorActivatedObjective(generatorActivatedObjective), + BaseHumanDamageObjective humanDamageObjective => new HumanDamageObjective(humanDamageObjective), + BaseHumanKillObjective humanKillObjective => new HumanKillObjective(humanKillObjective), + BaseEscapeObjective escapeObjective => new EscapeObjective(escapeObjective), + _ => new Objective(factionObjectiveBase) + }; + } + + /// + /// Reduces timer for faction. + /// + /// Faction to affect. + /// Time to reduce in seconds. + public void ReduceTimer(Faction faction, float seconds) => Base.ReduceTimer(faction, seconds); + + /// + /// Grants influence to faction. + /// + /// Faction to affect. + /// Amount of influence to grant. + public void GrantInfluence(Faction faction, float amount) => Base.GrantInfluence(faction, amount); + + /// + /// Achieves objective. + /// + public void Achieve() => Base.ServerSendUpdate(); + + /// + /// Checks if faction has this objective. + /// + /// Faction to check. + /// true if faction has this objective, false otherwise. + public bool IsValidFaction(Faction faction) => Base.IsValidFaction(faction); + + /// + /// Checks if player has this objective. + /// + /// Player to check. + /// true if player has this objective, false otherwise. + public bool IsValidFaction(Player player) => Base.IsValidFaction(player.ReferenceHub); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Objectives/ScpItemPickupObjective.cs b/EXILED/Exiled.API/Features/Objectives/ScpItemPickupObjective.cs new file mode 100644 index 000000000..a50a36057 --- /dev/null +++ b/EXILED/Exiled.API/Features/Objectives/ScpItemPickupObjective.cs @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Objectives +{ + using Exiled.API.Enums; + using Exiled.API.Features.Items; + using Exiled.API.Features.Pickups; + using Exiled.API.Interfaces; + using Respawning.Objectives; + using UnityEngine; + + using BaseObjective = Respawning.Objectives.ScpItemPickupObjective; + + /// + /// Represents an objective that is completed when a SCP item is picked up. + /// + public class ScpItemPickupObjective : HumanObjective, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// A instance. + internal ScpItemPickupObjective(BaseObjective objectiveFootprintBase) + : base(objectiveFootprintBase) + { + Base = objectiveFootprintBase; + } + + /// + public new BaseObjective Base { get; } + + /// + public override ObjectiveType Type { get; } = ObjectiveType.ScpItemPickup; + + /// + /// Fakes picking up an item and tries to achieve this objective. + /// + /// Target player. + /// Item that was picked up. + /// Pickup that was picked up. + public void AddItem(Player target, Item item, Pickup pickup) => Base.OnItemAdded(target.ReferenceHub, item.Base, pickup.Base); + + /// + /// Fakes picking up an item and tries to achieve this objective. + /// + /// Target player. + /// Item that was picked up. + public void AddItem(Player target, Item item) => Base.OnItemAdded(target.ReferenceHub, item.Base, item.CreatePickup(Vector3.zero, Quaternion.identity, false).Base); + + /// + /// Fakes picking up an item and tries to achieve this objective. + /// + /// Target player. + /// Pickup that was picked up. + public void AddItem(Player target, Pickup pickup) => Base.OnItemAdded(target.ReferenceHub, Item.Create(pickup.Type, target).Base, pickup.Base); + + /// + /// Fakes picking up an item and tries to achieve this objective. + /// + /// Target player. + /// Item that was picked up. + public void AddItem(Player target, ItemType type) => AddItem(target, Item.Create(type, target)); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Pickups/Pickup.cs b/EXILED/Exiled.API/Features/Pickups/Pickup.cs index 77998260a..fa9aca3b2 100644 --- a/EXILED/Exiled.API/Features/Pickups/Pickup.cs +++ b/EXILED/Exiled.API/Features/Pickups/Pickup.cs @@ -532,7 +532,7 @@ public static IEnumerable Get(IEnumerable gameObjects) /// The specified type. /// The created . /// - public static Pickup Create(ItemType type) + public static Pickup Create(ItemType type) // TODO modify return type to "T" where T : Pickup => Create(type) as T; /// diff --git a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs index 17a0691b9..80a129eb6 100644 --- a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs +++ b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs @@ -111,7 +111,7 @@ internal Projectile(ItemType type) /// The of the projectile. /// The specified type. /// The created . - public static Projectile Create(ProjectileType projectiletype) + public static Projectile Create(ProjectileType projectiletype) // TODO modify return type to "T" where T : Projectile => Create(projectiletype) as T; /// diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 1c1e7fb54..ada3c952a 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. @@ -2184,6 +2206,12 @@ public void Heal(float amount, bool overrideMaxHealth = false) /// if item was used successfully. Otherwise, . public bool UseItem(ItemType usableItem) => UseItem(Item.Create(usableItem)); + /// + /// Forces the player to use an item. + /// + /// The item to be used. + public void UseItem(Usable usable) => usable?.Use(this); + /// /// Forces the player to use an item. /// @@ -2194,14 +2222,7 @@ public bool UseItem(Item item) if (item is not Usable usableItem) return false; - usableItem.Base.Owner = referenceHub; - usableItem.Base.ServerOnUsingCompleted(); - - typeof(UsableItemsController).InvokeStaticEvent(nameof(UsableItemsController.ServerOnUsingCompleted), new object[] { referenceHub, usableItem.Base }); - - if (usableItem.Base is not null) - usableItem.Destroy(); - + UseItem(usableItem); return true; } diff --git a/EXILED/Exiled.API/Features/Roles/Role.cs b/EXILED/Exiled.API/Features/Roles/Role.cs index ef6d8dcea..31f53ac04 100644 --- a/EXILED/Exiled.API/Features/Roles/Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Role.cs @@ -27,6 +27,7 @@ namespace Exiled.API.Features.Roles using Scp079GameRole = PlayerRoles.PlayableScps.Scp079.Scp079Role; using Scp096GameRole = PlayerRoles.PlayableScps.Scp096.Scp096Role; using Scp106GameRole = PlayerRoles.PlayableScps.Scp106.Scp106Role; + using Scp1507GameRole = PlayerRoles.PlayableScps.Scp1507.Scp1507Role; using Scp173GameRole = PlayerRoles.PlayableScps.Scp173.Scp173Role; using Scp3114GameRole = PlayerRoles.PlayableScps.Scp3114.Scp3114Role; using Scp939GameRole = PlayerRoles.PlayableScps.Scp939.Scp939Role; @@ -235,7 +236,10 @@ public virtual void Set(RoleTypeId newRole, SpawnReason reason, RoleSpawnFlags s FilmmakerGameRole filmmakerRole => new FilmMakerRole(filmmakerRole), NoneGameRole noneRole => new NoneRole(noneRole), DestroyedGameRole destroyedRole => new DestroyedRole(destroyedRole), - _ => null, +#pragma warning disable CS0618 + Scp1507GameRole scp1507 => new Scp1507Role(scp1507), +#pragma warning restore CS0618 + _ => throw new Exception($"Missing role found in Exiled.API.Features.Roles.Role::Create ({role?.RoleTypeId}). Please contact an Exiled developer."), }; } } diff --git a/EXILED/Exiled.API/Features/Roles/Scp1507Role.cs b/EXILED/Exiled.API/Features/Roles/Scp1507Role.cs new file mode 100644 index 000000000..559fdd662 --- /dev/null +++ b/EXILED/Exiled.API/Features/Roles/Scp1507Role.cs @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Roles +{ + using System; + using System.Collections.Generic; + + using PlayerRoles; + using PlayerRoles.PlayableScps; + using PlayerRoles.PlayableScps.HumeShield; + using PlayerRoles.Subroutines; + + using Scp1507GameRole = PlayerRoles.PlayableScps.Scp1507.Scp1507Role; + + /// + /// Defines a role that represents SCP-1507. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class Scp1507Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp + { + /// + /// Initializes a new instance of the class. + /// + /// the base . + internal Scp1507Role(Scp1507GameRole baseRole) + : base(baseRole) + { + Base = baseRole; + SubroutineModule = baseRole.SubroutineModule; + HumeShieldModule = baseRole.HumeShieldModule; + } + + /// + public override RoleTypeId Type => Base._roleTypeId; + + /// + public SubroutineManagerModule SubroutineModule { get; } + + /// + public HumeShieldModuleBase HumeShieldModule { get; } + + /// + /// Gets the instance. + /// + public new Scp1507GameRole Base { get; } + + /// + /// Gets the Spawn Chance of SCP-939. + /// + /// The List of Roles already spawned. + /// The Spawn Chance. + public float GetSpawnChance(List alreadySpawned) => Base is ISpawnableScp spawnableScp ? spawnableScp.GetSpawnChance(alreadySpawned) : 0; + } +} 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/Scp3114Ragdoll.cs b/EXILED/Exiled.API/Features/Scp3114Ragdoll.cs index a8856d142..e8bb6a3f0 100644 --- a/EXILED/Exiled.API/Features/Scp3114Ragdoll.cs +++ b/EXILED/Exiled.API/Features/Scp3114Ragdoll.cs @@ -75,4 +75,4 @@ public bool IsPlayingAnimation set => Base._playingAnimation = value; } } -} \ No newline at end of file +} diff --git a/EXILED/Exiled.API/Features/Scp559.cs b/EXILED/Exiled.API/Features/Scp559.cs new file mode 100644 index 000000000..08fc89904 --- /dev/null +++ b/EXILED/Exiled.API/Features/Scp559.cs @@ -0,0 +1,137 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Interfaces; + using MapGeneration; + using UnityEngine; + + /// + /// Represents a cake. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class Scp559 : IWrapper, IPosition + { + /// + /// to . + /// + internal static readonly Dictionary CakeToWrapper = new(); + + /// + /// Initializes a new instance of the class. + /// + /// The instance. + public Scp559(Scp559Cake cakeBase) + { + Base = cakeBase; + + CakeToWrapper.Add(cakeBase, this); + } + + /// + /// Gets the list with all instances. + /// + public static IReadOnlyCollection List => CakeToWrapper.Values; + + /// + /// Gets a with rooms and amount of people in them. + /// + public static Dictionary PopulatedRooms => Scp559Cake.PopulatedRooms.ToDictionary(x => Room.Get(x.Key), x => x.Value); + + /// + /// Gets a with spawnpoint in rooms. + /// + public static Dictionary SpawnPositions => Scp559Cake.Spawnpoints; + + /// + /// Gets the list of all available spawnpoints. + /// + public static List AvailableSpawnpoints => Scp559Cake.PossiblePositions; + + /// + /// Gets or sets offset for spawning near pedestals. + /// + public static Vector3 PedestalOffset + { + get => Scp559Cake.PedestalOffset; + set => Scp559Cake.PedestalOffset.Set(value.x, value.y, value.z); + } + + /// + public Scp559Cake Base { get; } + + /// + /// Gets or sets how many slices are still on cake. + /// + public byte RemainingSlices + { + get => Base._remainingSlices; + set => Base.Network_remainingSlices = value; + } + + /// + /// Gets or sets a value indicating whether or not cake is spawned. + /// + public bool IsSpawned + { + get => Base._isSpawned; + set => Base.Network_isSpawned = value; + } + + /// + /// Gets or sets the time how much cake will still be usable. + /// + public float RemainingTime + { + get => Base._remainingTime; + set => Base._remainingTime = value; + } + + /// + /// Gets or sets the minimum required time for cake to spawn. + /// + public float RespawnTime + { + get => Base._respawnTime; + set => Base._respawnTime = value; + } + + /// + public Vector3 Position + { + get => Base._position; + set => Base.Network_position = value; + } + + /// + /// Gets the by it's game instance. + /// + /// Game instance. + /// . + public static Scp559 Get(Scp559Cake cake) => CakeToWrapper.TryGetValue(cake, out Scp559 scp559) ? scp559 : new Scp559(cake); + + /// + /// Gets the of SCP-559 which matches the predicate. + /// + /// Predicate to match. + /// of SCP-559. + public static IEnumerable Get(Func predicate) => List.Where(predicate); + + /// + /// Tries to get available spawn point for SCP-559. + /// + /// Position of spawn. + /// Will be pedestal also spawned. + /// if position was found. Otherwise, . + public bool TryGetSpawnpoint(out Vector3 pos, out bool pedestal) => Base.TryGetSpawnPoint(out pos, out pedestal); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Scp956.cs b/EXILED/Exiled.API/Features/Scp956.cs new file mode 100644 index 000000000..49c90a5b3 --- /dev/null +++ b/EXILED/Exiled.API/Features/Scp956.cs @@ -0,0 +1,110 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features +{ + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Enums; + using Exiled.API.Extensions; + using UnityEngine; + + /// + /// A wrapper for . + /// + public static class Scp956 + { + /// + /// Gets the instance. + /// + public static Scp956Pinata Singleton => Scp956Pinata._instance; + + /// + /// Gets or sets a value indicating whether or not SCP-956 is spawned. + /// + public static bool IsSpawned + { + get => Singleton._spawned; + set => Singleton.Network_spawned = value; + } + + /// + /// Gets or sets current position. + /// + public static Vector3 Position + { + get => Singleton._syncPos; + set => Singleton.Network_syncPos = value; + } + + /// + /// Gets or sets initial position. + /// + public static Vector3 InitPos + { + get => Singleton._initialPos; + set => Singleton._initialPos = value; + } + + /// + /// Gets or sets current rotation. + /// + public static float Rotation + { + get => Singleton._syncRot; + set => Singleton.Network_syncRot = value; + } + + /// + /// Gets or sets initial rotation. + /// + public static float InitRotation + { + get => Singleton._initialRot; + set => Singleton._initialRot = value; + } + + /// + /// Gets or sets a value indicating whether or not SCP-956 is flying. + /// + public static bool IsFlying + { + get => Singleton._flying; + set => Singleton.Network_flying = value; + } + + /// + /// Gets or sets current target of an SCP. + /// + public static Scp956Target CurrentTarget + { + get => Singleton._foundTarget; + set => Singleton._foundTarget = value; + } + + /// + /// Gets or sets a value indicating whether or not SCP-956 should look like a capybara. + /// + public static bool IsCapybara { get; set; } = false; + + /// + /// Gets or sets zones where SCP-956 can spawn. + /// + public static IEnumerable AvailableZones + { + get => Singleton._spawnableZones.Select(x => x.GetZone()); + set => Singleton._spawnableZones = value.Select(x => x.GetZone()).ToArray(); + } + + /// + /// Spawns behind the specified target. + /// + /// Player to spawn. If is , will be chosen random. + public static void SpawnBehindTarget(Player target = null) => Singleton.SpawnBehindTarget((target ?? Player.List.GetRandomValue()).ReferenceHub); + } +} \ No newline at end of file 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.API/Features/Warhead.cs b/EXILED/Exiled.API/Features/Warhead.cs index a7369cfab..687b4fb03 100644 --- a/EXILED/Exiled.API/Features/Warhead.cs +++ b/EXILED/Exiled.API/Features/Warhead.cs @@ -35,13 +35,18 @@ public static class Warhead /// /// Gets the cached component. /// - public static AlphaWarheadOutsitePanel OutsitePanel => alphaWarheadOutsitePanel != null ? alphaWarheadOutsitePanel : (alphaWarheadOutsitePanel = Object.FindObjectOfType()); + public static AlphaWarheadOutsitePanel OutsitePanel => alphaWarheadOutsitePanel != null ? alphaWarheadOutsitePanel : (alphaWarheadOutsitePanel = UnityEngine.Object.FindObjectOfType()); /// /// Gets the of the warhead lever. /// public static GameObject Lever => SitePanel.lever.gameObject; + /// + /// Gets or sets a value indicating whether DeadmanSwitch detonation is enabled. + /// + public static bool DeadmanSwitchEnabled { get; set; } = true; + /// /// Gets or sets a value indicating whether automatic detonation is enabled. /// diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs index 5eea6969b..8aff47f91 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs @@ -26,11 +26,8 @@ namespace Exiled.CustomItems.API.Features using Exiled.Events.EventArgs.Scp914; using Exiled.Loader; - using InventorySystem.Items.Firearms; using InventorySystem.Items.Pickups; - using MapGeneration.Distributors; - using MEC; using PlayerRoles; @@ -651,8 +648,11 @@ public virtual uint Spawn(IEnumerable spawnPoints, uint limit) if (pickup == null) continue; - if (spawnPoint is LockerSpawnPoint) - pickup.IsLocked = true; + if (spawnPoint is LockerSpawnPoint { UseChamber: true } lockerSpawnPoint) + { + Exiled.API.Features.Lockers.Locker? foundLocker = Exiled.API.Features.Lockers.Locker.Random(lockerSpawnPoint.Zone, lockerSpawnPoint.Type); + foundLocker?.AddItem(pickup); + } if (pickup.Is(out Exiled.API.Features.Pickups.FirearmPickup firearmPickup) && this is CustomWeapon customWeapon) { @@ -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.CustomItems/API/Features/CustomWeapon.cs b/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs index dea5aec5c..96122fa64 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs @@ -8,8 +8,8 @@ namespace Exiled.CustomItems.API.Features { using System; + using System.Linq; - using Exiled.API.Enums; using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.DamageHandlers; @@ -19,8 +19,7 @@ namespace Exiled.CustomItems.API.Features using InventorySystem.Items.Firearms.Attachments; using InventorySystem.Items.Firearms.Attachments.Components; - using InventorySystem.Items.Firearms.BasicMessages; - + using InventorySystem.Items.Firearms.Modules; using UnityEngine; using Firearm = Exiled.API.Features.Items.Firearm; @@ -57,6 +56,8 @@ public override ItemType Type /// /// Gets or sets a value indicating how big of a clip the weapon will have. /// + /// Warning for and . + /// They are not fully compatible with this features. public virtual byte ClipSize { get; set; } /// @@ -76,9 +77,6 @@ public override ItemType Type if (!Attachments.IsEmpty()) firearm.AddAttachment(Attachments); - firearm.MagazineAmmo = ClipSize; - firearm.MaxMagazineAmmo = ClipSize; - Pickup? pickup = firearm.CreatePickup(position); if (pickup is null) @@ -87,6 +85,9 @@ public override ItemType Type return null; } + if (ClipSize > 0) + firearm.MagazineAmmo = ClipSize; + pickup.Weight = Weight; pickup.Scale = Scale; if (previousOwner is not null) @@ -104,8 +105,9 @@ public override ItemType Type if (!Attachments.IsEmpty()) firearm.AddAttachment(Attachments); + if (ClipSize > 0) + firearm.MagazineAmmo = ClipSize; int ammo = firearm.MagazineAmmo; - firearm.MaxMagazineAmmo = ClipSize; Log.Debug($"{nameof(Name)}.{nameof(Spawn)}: Spawning weapon with {ammo} ammo."); Pickup? pickup = firearm.CreatePickup(position); pickup.Scale = Scale; @@ -130,8 +132,8 @@ public override void Give(Player player, bool displayMessage = true) if (!Attachments.IsEmpty()) firearm.AddAttachment(Attachments); - firearm.MagazineAmmo = ClipSize; - firearm.MaxMagazineAmmo = ClipSize; + if (ClipSize > 0) + firearm.MagazineAmmo = ClipSize; } Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker."); @@ -144,6 +146,7 @@ public override void Give(Player player, bool displayMessage = true) protected override void SubscribeEvents() { Exiled.Events.Handlers.Player.ReloadingWeapon += OnInternalReloading; + Exiled.Events.Handlers.Player.ReloadedWeapon += OnInternalReloaded; Exiled.Events.Handlers.Player.Shooting += OnInternalShooting; Exiled.Events.Handlers.Player.Shot += OnInternalShot; Exiled.Events.Handlers.Player.Hurting += OnInternalHurting; @@ -155,6 +158,7 @@ protected override void SubscribeEvents() protected override void UnsubscribeEvents() { Exiled.Events.Handlers.Player.ReloadingWeapon -= OnInternalReloading; + Exiled.Events.Handlers.Player.ReloadedWeapon -= OnInternalReloaded; Exiled.Events.Handlers.Player.Shooting -= OnInternalShooting; Exiled.Events.Handlers.Player.Shot -= OnInternalShot; Exiled.Events.Handlers.Player.Hurting -= OnInternalHurting; @@ -170,6 +174,14 @@ protected virtual void OnReloading(ReloadingWeaponEventArgs ev) { } + /// + /// Handles reloaded for custom weapons. + /// + /// . + protected virtual void OnReloaded(ReloadedWeaponEventArgs ev) + { + } + /// /// Handles shooting for custom weapons. /// @@ -201,49 +213,29 @@ private void OnInternalReloading(ReloadingWeaponEventArgs ev) if (!Check(ev.Player.CurrentItem)) return; - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: Reloading weapon. Calling external reload event.."); - OnReloading(ev); - - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: External event ended. {ev.IsAllowed}"); - if (!ev.IsAllowed) + if (ClipSize > 0 && ev.Firearm.Base.GetTotalStoredAmmo() >= ClipSize) { - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: External event turned is allowed to false, returning."); + ev.IsAllowed = false; return; } - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: Continuing with internal reload.."); - ev.IsAllowed = false; - - int remainingClip = ((Firearm)ev.Player.CurrentItem).MagazineAmmo; + OnReloading(ev); + } - if (remainingClip >= ClipSize) + private void OnInternalReloaded(ReloadedWeaponEventArgs ev) + { + if (!Check(ev.Player.CurrentItem)) return; - Log.Debug($"{ev.Player.Nickname} ({ev.Player.UserId}) [{ev.Player.Role}] is reloading a {Name} ({Id}) [{Type} ({remainingClip}/{ClipSize})]!"); - - AmmoType ammoType = ev.Firearm.AmmoType; - - if (!ev.Player.Ammo.ContainsKey(ammoType.GetItemType())) + if (ClipSize > 0) { - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: {ev.Player.Nickname} does not have ammo to reload this weapon."); - return; + int ammoChambered = ((AutomaticActionModule)ev.Firearm.Base.Modules.FirstOrDefault(x => x is AutomaticActionModule))?.SyncAmmoChambered ?? 0; + int ammodrop = -(ClipSize - ev.Firearm.MagazineAmmo) - ammoChambered; + ev.Firearm.MagazineAmmo = ClipSize - ammoChambered; + ev.Player.AddAmmo(ev.Firearm.AmmoType, (ushort)Mathf.Clamp(ammodrop, ushort.MinValue, ushort.MaxValue)); } - ev.Firearm.Reload(); - - byte amountToReload = (byte)Math.Min(ClipSize - remainingClip, ev.Player.Ammo[ammoType.GetItemType()]); - - if (amountToReload <= 0) - return; - - ev.Player.ReferenceHub.playerEffectsController.GetEffect().Intensity = 0; - - ev.Player.Ammo[ammoType.GetItemType()] -= amountToReload; - ev.Player.Inventory.SendAmmoNextFrame = true; - - ((Firearm)ev.Player.CurrentItem).MagazineAmmo = (byte)(((Firearm)ev.Player.CurrentItem).MagazineAmmo + amountToReload); - - Log.Debug($"{ev.Player.Nickname} ({ev.Player.UserId}) [{ev.Player.Role}] reloaded a {Name} ({Id}) [{Type} ({((Firearm)ev.Player.CurrentItem).MagazineAmmo}/{ClipSize})]!"); + OnReloaded(ev); } private void OnInternalShooting(ShootingEventArgs ev) 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/Interfaces/IScp1507Event.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs new file mode 100644 index 000000000..1d6dcf3ad --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs @@ -0,0 +1,25 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Interfaces +{ + using System; + + using Exiled.API.Features.Roles; + + /// + /// Event args used for all related events. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public interface IScp1507Event : IPlayerEvent + { + /// + /// Gets the triggering the event. + /// + public Scp1507Role Scp1507 { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IScp559Event.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp559Event.cs new file mode 100644 index 000000000..d8a553725 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp559Event.cs @@ -0,0 +1,23 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Interfaces +{ + using System; + + /// + /// Defines the base contract for all related events. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public interface IScp559Event : IExiledEvent + { + /// + /// Gets the . + /// + public API.Features.Scp559 Scp559 { get; } + } +} \ No newline at end of file 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/ElevatorSequencesUpdatedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/ElevatorSequencesUpdatedEventArgs.cs new file mode 100644 index 000000000..c2a8fbe9a --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Map/ElevatorSequencesUpdatedEventArgs.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Map +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using Interactables.Interobjects; + + /// + /// Contains all information after an elevator sequence is updated. + /// + public class ElevatorSequencesUpdatedEventArgs : IExiledEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ElevatorSequencesUpdatedEventArgs(ElevatorChamber elevatorChamber, ElevatorChamber.ElevatorSequence sequence) + { + Elevator = elevatorChamber; + Lift = Lift.Get(elevatorChamber); + Sequence = sequence; + } + + /// + /// Gets the elevator chamber that triggered this event. + /// + public ElevatorChamber Elevator { get; } + + /// + /// Gets the lift that triggered this event. + /// + public Lift Lift { get; } + + /// + /// Gets the new sequence of the elevator. + /// + public ElevatorChamber.ElevatorSequence Sequence { get; } + } +} \ No newline at end of file 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/DrinkingCoffeeEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DrinkingCoffeeEventArgs.cs new file mode 100644 index 000000000..f830fb4ed --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/DrinkingCoffeeEventArgs.cs @@ -0,0 +1,42 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before player interacts with coffee cup. + /// + public class DrinkingCoffeeEventArgs : IPlayerEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public DrinkingCoffeeEventArgs(Player player, Coffee coffee, bool isAllowed = true) + { + Player = player; + Coffee = coffee; + IsAllowed = isAllowed; + } + + /// + /// Gets the coffee with which player is interacting. + /// + public Coffee Coffee { get; } + + /// + public Player Player { get; } + + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file 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/EventArgs/Player/PreAuthenticatingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs index 9e5ed782e..f2af6a830 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.EventArgs.Player using Interfaces; using LiteNetLib; - + using LiteNetLib.Utils; using PluginAPI.Events; #pragma warning disable SA1600 //TODO: @@ -27,8 +27,6 @@ namespace Exiled.Events.EventArgs.Player /// public class PreAuthenticatingEventArgs : IExiledEvent { - private PreauthCancellationData cachedPreauthData = PreauthCancellationData.Accept(); - /// /// Initializes a new instance of the class. /// @@ -105,70 +103,6 @@ public PreAuthenticatingEventArgs( /// public bool IsAllowed { get; private set; } = true; - /// - /// Gets or sets the cached that is returned back to the NwPluginAPI. - /// - internal PreauthCancellationData CachedPreauthData - { - get => cachedPreauthData; - set - { - cachedPreauthData = value; - IsAllowed = false; - } - } - - /// - /// Delays a pre-authentincating player. - /// - /// The seconds of delay. - /// Indicates whether the delay is forced. - public void Delay(byte seconds, bool isForced) => - CachedPreauthData = PreauthCancellationData.RejectDelay(seconds, isForced); - - /// - /// Redirects a pre-authentincating player. - /// - /// The redirection port. - /// Indicates whether the redirection is forced. - public void Redirect(ushort port, bool isForced) => - CachedPreauthData = PreauthCancellationData.RejectRedirect(port, isForced); - - /// - /// Rejects a pre-authentincating banned player. - /// - /// The ban reason.> - /// The ban expiration. - /// Indicates whether the rejection is forced. - public void RejectBanned(string banReason, DateTime expiration, bool isForced) => - CachedPreauthData = PreauthCancellationData.RejectBanned(banReason, expiration, isForced); - - /// - /// Rejects a pre-authentincating banned player. - /// - /// The ban reason. - /// The ban expiration. - /// Indicates whether the rejection is forced. - public void RejectBanned(string banReason, long expiration, bool isForced) => - CachedPreauthData = PreauthCancellationData.RejectBanned(banReason, expiration, isForced); - - /// - /// Rejects a pre-authentincating player. - /// - /// The rejection custom reason. - /// Indicates whether the rejection is forced. - public void Reject(string customReason, bool isForced) => - CachedPreauthData = PreauthCancellationData.Reject(customReason, isForced); - - /// - /// Rejects a pre-authentincating player. - /// - /// The . - /// Indicates whether the rejection is forced. - public void Reject(RejectionReason reason, bool isForced) => - CachedPreauthData = PreauthCancellationData.Reject(reason, isForced); - - /* /// /// Delays the connection. /// @@ -230,6 +164,11 @@ public void Reject(NetDataWriter writer, bool isForced) /// Indicates whether the player has to be rejected forcefully. public void Reject(string rejectionReason, bool isForced) => Reject(RejectionReason.Custom, isForced, rejectionReason); + /// + /// Rejects a player who's trying to authenticate. + /// + public void ForceReject() => Reject(RejectionReason.Custom, true, "Rejected By A Plugin"); + /// /// Rejects a player who's trying to authenticate. /// @@ -278,12 +217,5 @@ public void Reject(RejectionReason rejectionReason, bool isForced, string custom else Request.Reject(rejectData); } - - /// - /// Disallows the connection without sending any reason. Should only be used when the connection has already been - /// terminated by the plugin itself. - /// - public void Disallow() => IsAllowed = false; - */ } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReloadedWeaponEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReloadedWeaponEventArgs.cs new file mode 100644 index 000000000..464f2db6c --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/ReloadedWeaponEventArgs.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using API.Features; + using API.Features.Items; + + using Interfaces; + + /// + /// Contains all information after a player's weapon is reloaded. + /// + public class ReloadedWeaponEventArgs : IPlayerEvent, IFirearmEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public ReloadedWeaponEventArgs(InventorySystem.Items.Firearms.Firearm firearm) + { + Firearm = Item.Get(firearm); + Player = Firearm.Owner; + } + + /// + /// Gets the being reloaded. + /// + public Firearm Firearm { get; } + + /// + public Item Item => Firearm; + + /// + /// Gets the player who's reloading the weapon. + /// + public Player Player { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs new file mode 100644 index 000000000..a3fa2d531 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs @@ -0,0 +1,79 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using CommandSystem; + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using Exiled.Events.EventArgs.Interfaces; + using PluginAPI.Enums; + using RemoteAdmin; + + /// + /// Contains all information before a player sends the command. + /// + public class SendingValidCommandEventArgs : IPlayerEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SendingValidCommandEventArgs(Player player, ICommand command, CommandType commandType, string query, string response) + { + Player = player; + Command = command; + Type = commandType; + Query = query; + Response = response; + } + + /// + /// Gets or sets a value indicating whether the player can send the command. + /// + public bool IsAllowed { get; set; } = true; + + /// + /// Gets or sets the response of the command. If this value is null, the response will stay unchanged. + /// + public string Response { get; set; } + + /// + /// Gets the player who is sending the command. + /// + public Player Player { get; } + + /// + /// Gets the command query. + /// + public string Query { get; } + + /// + /// Gets the command type. + /// + public CommandType Type { get; } + + /// + /// Gets the command interface. + /// + public ICommand Command { get; } + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs new file mode 100644 index 000000000..0fb7778a1 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using CommandSystem; + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using Exiled.Events.EventArgs.Interfaces; + using PluginAPI.Enums; + using RemoteAdmin; + + /// + /// Contains all information after a player sends the command. + /// + public class SentValidCommandEventArgs : IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SentValidCommandEventArgs(Player player, ICommand command, CommandType commandType, string query, string response, bool result) + { + Player = player; + Command = command; + Type = commandType; + Query = query; + Response = response; + Result = result; + } + + /// + /// Gets a value indicating whether the command succeeded. + /// + public bool Result { get; } + + /// + /// Gets the response of the command. + /// + public string Response { get; } + + /// + /// Gets the player who is sending the command. + /// + public Player Player { get; } + + /// + /// Gets the command query. + /// + public string Query { get; } + + /// + /// Gets the command type. + /// + public CommandType Type { get; } + + /// + /// Gets the command interface. + /// + public ICommand Command { get; } + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Player/UnloadedWeaponEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UnloadedWeaponEventArgs.cs new file mode 100644 index 000000000..69a37671d --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/UnloadedWeaponEventArgs.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using API.Features; + using API.Features.Items; + + using Interfaces; + + /// + /// Contains all information after a player's weapon is unloaded. + /// + public class UnloadedWeaponEventArgs : IPlayerEvent, IFirearmEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public UnloadedWeaponEventArgs(InventorySystem.Items.Firearms.Firearm firearm) + { + Firearm = Item.Get(firearm); + Player = Firearm.Owner; + } + + /// + /// Gets the being unloaded. + /// + public Firearm Firearm { get; } + + /// + public Item Item => Firearm; + + /// + /// Gets the player who's unloading the weapon. + /// + public Player Player { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/LosingSignalEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/LosingSignalEventArgs.cs new file mode 100644 index 000000000..cae1c775e --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp079/LosingSignalEventArgs.cs @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp079 +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before SCP-079 loses a signal by SCP-2176. + /// + public class LosingSignalEventArgs : IScp079Event, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public LosingSignalEventArgs(ReferenceHub player) + { + Player = Player.Get(player); + Scp079 = Player.Role.As(); + IsAllowed = true; + } + + /// + /// Gets the player who's controlling SCP-079. + /// + public Player Player { get; } + + /// + /// Gets or sets a value indicating whether or not SCP-079 will lose his signal. + /// + public bool IsAllowed { get; set; } + + /// + public API.Features.Roles.Scp079Role Scp079 { get; } + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/LostSignalEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/LostSignalEventArgs.cs new file mode 100644 index 000000000..247eb5c20 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp079/LostSignalEventArgs.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp079 +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before SCP-079 loses a signal by SCP-2176. + /// + public class LostSignalEventArgs : IScp079Event + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public LostSignalEventArgs(ReferenceHub player) + { + Player = Player.Get(player); + Scp079 = Player.Role.As(); + } + + /// + /// Gets the player who's controlling SCP-079. + /// + public Player Player { get; } + + /// + public API.Features.Roles.Scp079Role Scp079 { get; } + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/StartingSpeakerEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/StartingSpeakerEventArgs.cs index fe160a28a..e69de29bb 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp079/StartingSpeakerEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp079/StartingSpeakerEventArgs.cs @@ -1,66 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.EventArgs.Scp079 -{ - using Exiled.API.Features; - using Exiled.API.Features.Roles; - using Exiled.Events.EventArgs.Interfaces; - - /// - /// Contains all information before SCP-079 uses a speaker. - /// - public class StartingSpeakerEventArgs : IScp079Event, IDeniableEvent - { - /// - /// Initializes a new instance of the class. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public StartingSpeakerEventArgs(Player player, Room room, float auxiliaryPowerCost, bool isAllowed = true) - { - Player = player; - Scp079 = player.Role.As(); - Room = room; - AuxiliaryPowerCost = auxiliaryPowerCost; - IsAllowed = isAllowed; - } - - /// - /// Gets the player who's controlling SCP-079. - /// - public Player Player { get; } - - /// - public Scp079Role Scp079 { get; } - - /// - /// Gets the room that the speaker is located in. - /// - public Room Room { get; } - - /// - /// Gets or sets the amount of auxiliary power required to use a speaker through SCP-079. - /// - public float AuxiliaryPowerCost { get; set; } - - /// - /// Gets or sets a value indicating whether SCP-079 can use the speaker. - /// - public bool IsAllowed { get; set; } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/StoppingSpeakerEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/StoppingSpeakerEventArgs.cs index 9191c4cc0..e69de29bb 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp079/StoppingSpeakerEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp079/StoppingSpeakerEventArgs.cs @@ -1,57 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.EventArgs.Scp079 -{ - using Exiled.API.Features; - using Exiled.API.Features.Roles; - using Exiled.Events.EventArgs.Interfaces; - - /// - /// Contains all information before SCP-079 finishes using a speaker. - /// - public class StoppingSpeakerEventArgs : IScp079Event, IDeniableEvent - { - /// - /// Initializes a new instance of the class. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public StoppingSpeakerEventArgs(Player player, Room room, bool isAllowed = true) - { - Player = player; - Scp079 = player.Role.As(); - Room = room; - IsAllowed = isAllowed; - } - - /// - /// Gets the player who's controlling SCP-079. - /// - public Player Player { get; } - - /// - public Scp079Role Scp079 { get; } - - /// - /// Gets the room that the speaker is located in. - /// - public Room Room { get; } - - /// - /// Gets or sets a value indicating whether SCP-079 can stop using the speaker. - /// - public bool IsAllowed { get; set; } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs new file mode 100644 index 000000000..c58330e25 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs @@ -0,0 +1,50 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + using System; + + using Exiled.API.Features; + using Exiled.API.Features.Doors; + using Exiled.API.Features.Roles; + using Exiled.Events.EventArgs.Interfaces; + using Interactables.Interobjects.DoorUtils; + + /// + /// Contains all information before SCP-1507 attacks door. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class AttackingDoorEventArgs : IScp1507Event, IDeniableEvent, IDoorEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public AttackingDoorEventArgs(Player player, DoorVariant doorVariant, bool isAllowed = true) + { + Player = player; + Scp1507 = player.Role.As(); + Door = Door.Get(doorVariant); + IsAllowed = isAllowed; + } + + /// + public Player Player { get; } + + /// + public Scp1507Role Scp1507 { get; } + + /// + public bool IsAllowed { get; set; } + + /// + public Door Door { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs new file mode 100644 index 000000000..ae5ebb845 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + using System; + + using Exiled.API.Features; + using Exiled.API.Features.Roles; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before SCP-1507 screams. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class ScreamingEventArgs : IScp1507Event, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ScreamingEventArgs(Player player, bool isAllowed = true) + { + Player = player; + Scp1507 = player.Role.As(); + IsAllowed = isAllowed; + } + + /// + public Player Player { get; } + + /// + public Scp1507Role Scp1507 { get; } + + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp1507/SpawningFlamingosEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1507/SpawningFlamingosEventArgs.cs new file mode 100644 index 000000000..376eba5ab --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/SpawningFlamingosEventArgs.cs @@ -0,0 +1,50 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles.PlayableScps.Scp1507; + using Utils.NonAllocLINQ; + + /// + /// Contains all information before flamingos get spawned. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class SpawningFlamingosEventArgs : IDeniableEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public SpawningFlamingosEventArgs(Player newAlpha, bool isAllowed = true) + { + Player = newAlpha; + SpawnablePlayers = ReferenceHub.AllHubs.Where(Scp1507Spawner.ValidatePlayer).Select(x => Player.Get(x)).ToHashSet(); + IsAllowed = isAllowed; + } + + /// + /// Gets or sets the player which is being spawned as a new alpha flamingo. + /// + public Player Player { get; set; } + + /// + /// Gets or sets all enqueued spawnable players. + /// + public HashSet SpawnablePlayers { get; set; } + + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp1507/UsingTapeEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1507/UsingTapeEventArgs.cs new file mode 100644 index 000000000..0121d02d9 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/UsingTapeEventArgs.cs @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + using System; + + using Exiled.API.Features; + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items; + + /// + /// Contains all information before SCP-1507 screams. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class UsingTapeEventArgs : IPlayerEvent, IItemEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public UsingTapeEventArgs(ItemBase itemBase, bool isAllowed = true) + { + Item = Item.Get(itemBase); + Player = Item.Owner; + IsAllowed = isAllowed; + } + + /// + public Player Player { get; } + + /// + public Item Item { get; } + + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp2536/FindingPositionEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp2536/FindingPositionEventArgs.cs new file mode 100644 index 000000000..1bb577318 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp2536/FindingPositionEventArgs.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp2536 +{ + using Christmas.Scp2536; + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before SCP-2536 chooses target for spawning. + /// + public class FindingPositionEventArgs : IDeniableEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public FindingPositionEventArgs(Player player, Scp2536Spawnpoint spawnpoint, bool isAllowed = true) + { + Player = player; + Spawnpoint = spawnpoint; + IsAllowed = isAllowed; + } + + /// + public bool IsAllowed { get; set; } + + /// + /// Gets the player near whom SCP-2536 will spawn. + /// + public Player Player { get; } + + /// + /// Gets or sets the spawn point where SCP will spawn. + /// + public Scp2536Spawnpoint Spawnpoint { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp2536/GrantingGiftEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp2536/GrantingGiftEventArgs.cs new file mode 100644 index 000000000..4ecd4af40 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp2536/GrantingGiftEventArgs.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp2536 +{ + using Christmas.Scp2536; + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before player receives a gift from SCP-2536. + /// + public class GrantingGiftEventArgs : IDeniableEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public GrantingGiftEventArgs(Player player, Scp2536GiftBase gift, bool isAllowed = true) + { + Player = player; + Gift = gift; + IsAllowed = isAllowed; + } + + /// + public bool IsAllowed { get; set; } + + /// + public Player Player { get; } + + /// + /// Gets or sets a gift that will be granted to a . + /// + public Scp2536GiftBase Gift { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp2536/OpeningGiftEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp2536/OpeningGiftEventArgs.cs new file mode 100644 index 000000000..b952ed7fe --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp2536/OpeningGiftEventArgs.cs @@ -0,0 +1,36 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp2536 +{ + using Christmas.Scp2536; + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before player receives a gift from SCP-2536. + /// + public class OpeningGiftEventArgs : IDeniableEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public OpeningGiftEventArgs(Player player, bool isAllowed = true) + { + Player = player; + IsAllowed = isAllowed; + } + + /// + public bool IsAllowed { get; set; } + + /// + public Player Player { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp559/InteractingScp559EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp559/InteractingScp559EventArgs.cs new file mode 100644 index 000000000..b8b99f9ac --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp559/InteractingScp559EventArgs.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp559 +{ + using System; + + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before a player interacts with SCP-559. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class InteractingScp559EventArgs : IScp559Event, IDeniableEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public InteractingScp559EventArgs(Scp559 scp559, Player player, bool isAllowed = true) + { + Player = player; + Scp559 = scp559; + IsAllowed = isAllowed; + } + + /// + public Scp559 Scp559 { get; } + + /// + public bool IsAllowed { get; set; } + + /// + public Player Player { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp559/SpawningEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp559/SpawningEventArgs.cs new file mode 100644 index 000000000..6e3a81f06 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp559/SpawningEventArgs.cs @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp559 +{ + using System; + + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using UnityEngine; + + /// + /// Contains all information before SCP-559 spawns. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public class SpawningEventArgs : IDeniableEvent, IScp559Event + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public SpawningEventArgs(Scp559 scp559, Vector3 oldPosition, Vector3 newPosition, bool isAllowed = true) + { + Scp559 = scp559; + NextPosition = newPosition; + PreviousPosition = oldPosition; + IsAllowed = isAllowed; + } + + /// + public bool IsAllowed { get; set; } + + /// + public Scp559 Scp559 { get; } + + /// + /// Gets or sets the next spawn position. + /// + public Vector3 NextPosition { get; set; } + + /// + /// Gets the previous spawn position. + /// + public Vector3 PreviousPosition { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/CreatedAmnesticCloudEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/CreatedAmnesticCloudEventArgs.cs new file mode 100644 index 000000000..83a9d659f --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp939/CreatedAmnesticCloudEventArgs.cs @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp939 +{ + using API.Features; + + using Exiled.API.Features.Hazards; + using Exiled.API.Features.Roles; + using Interfaces; + + using PlayerRoles.PlayableScps.Scp939; + + using Scp939Role = API.Features.Roles.Scp939Role; + + /// + /// Contains all information after SCP-939 fully created target . + /// + public class CreatedAmnesticCloudEventArgs : IScp939Event, IHazardEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + /// + /// + public CreatedAmnesticCloudEventArgs(ReferenceHub hub, Scp939AmnesticCloudInstance cloud) + { + Player = Player.Get(hub); + AmnesticCloud = Hazard.Get(cloud); + Scp939 = Player.Role.As(); + } + + /// + public Player Player { get; } + + /// + /// Gets the instance. + /// + public AmnesticCloudHazard AmnesticCloud { get; } + + /// + public Scp939Role Scp939 { get; } + + /// + public Hazard Hazard => AmnesticCloud; + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs index d49aee7fa..d2545aad3 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs @@ -17,7 +17,7 @@ namespace Exiled.Events.EventArgs.Scp939 /// /// Contains all information after SCP-939 used its amnestic cloud ability. /// - public class PlacedAmnesticCloudEventArgs : IScp939Event + public class PlacedAmnesticCloudEventArgs : IScp939Event, IHazardEvent { /// /// Initializes a new instance of the class. @@ -26,12 +26,12 @@ public class PlacedAmnesticCloudEventArgs : IScp939Event /// /// /// - /// + /// /// public PlacedAmnesticCloudEventArgs(ReferenceHub hub, Scp939AmnesticCloudInstance cloud) { Player = Player.Get(hub); - AmnesticCloud = new AmnesticCloudHazard(cloud); + AmnesticCloud = Hazard.Get(cloud); Scp939 = Player.Role.As(); } @@ -47,5 +47,8 @@ public PlacedAmnesticCloudEventArgs(ReferenceHub hub, Scp939AmnesticCloudInstanc /// public Scp939Role Scp939 { get; } + + /// + public Hazard Hazard => AmnesticCloud; } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/ValidatingVisibilityEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/ValidatingVisibilityEventArgs.cs index a14873c9a..fa3a93f30 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp939/ValidatingVisibilityEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp939/ValidatingVisibilityEventArgs.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. @@ -8,41 +8,45 @@ namespace Exiled.Events.EventArgs.Scp939 { using API.Features; + + using Exiled.API.Enums; using Exiled.API.Features.Roles; using Interfaces; /// - /// Contains all information before SCP-939 sees the player. + /// Contains all information before SCP-939 sees the player. /// public class ValidatingVisibilityEventArgs : IScp939Event, IDeniableEvent { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// + /// + /// + /// /// - /// + /// /// /// - /// The target being shown to SCP-939. + /// The target being shown to SCP-939. /// - /// - /// Whether SCP-939 is allowed to view the player. - /// - public ValidatingVisibilityEventArgs(ReferenceHub player, ReferenceHub target, bool isAllowed) + public ValidatingVisibilityEventArgs(Scp939VisibilityState state, ReferenceHub player, ReferenceHub target) { Player = Player.Get(player); Scp939 = Player.Role.As(); Target = Player.Get(target); - IsAllowed = isAllowed; + TargetVisibilityState = state; + IsAllowed = TargetVisibilityState is not(Scp939VisibilityState.NotSeen or Scp939VisibilityState.None); + IsLateSeen = TargetVisibilityState is Scp939VisibilityState.SeenByRange; } /// - /// Gets the player who's being shown to SCP-939. + /// Gets the player who's being shown to SCP-939. /// public Player Target { get; } /// - /// Gets the player who's controlling SCP-939. + /// Gets the player who's controlling SCP-939. /// public Player Player { get; } @@ -50,8 +54,19 @@ public ValidatingVisibilityEventArgs(ReferenceHub player, ReferenceHub target, b public Scp939Role Scp939 { get; } /// - /// Gets or sets a value indicating whether visibility can be validated. + /// Gets the info about base-game vision information. /// - public bool IsAllowed { get; set; } + public Scp939VisibilityState TargetVisibilityState { get; } + + /// + /// Gets or sets a value indicating whether or not the SCP-939 will detect that vision as . + /// + /// + /// Works only when = , and makes player visible to SCP-939 for a while, after it's out of range. + /// + public bool IsLateSeen { get; set; } + + /// + public bool IsAllowed { get; set; } } -} \ No newline at end of file +} diff --git a/EXILED/Exiled.Events/EventArgs/Server/CompletingObjectiveEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/CompletingObjectiveEventArgs.cs new file mode 100644 index 000000000..def486974 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Server/CompletingObjectiveEventArgs.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using Exiled.API.Features.Objectives; + using Exiled.Events.EventArgs.Interfaces; + using Respawning.Objectives; + + /// + /// Contains all information before the completion of an objective. + /// + public class CompletingObjectiveEventArgs : IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public CompletingObjectiveEventArgs(FactionObjectiveBase objective, bool isAllowed = true) + { + Objective = Objective.Get(objective); + IsAllowed = isAllowed; + } + + /// + /// Gets the objective that is being completed. + /// + public Objective Objective { get; } + + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Warhead/DeadmanSwitchInitiatingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Warhead/DeadmanSwitchInitiatingEventArgs.cs new file mode 100644 index 000000000..389677132 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Warhead/DeadmanSwitchInitiatingEventArgs.cs @@ -0,0 +1,20 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Warhead +{ + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before detonating the warhead. + /// + public class DeadmanSwitchInitiatingEventArgs : IDeniableEvent + { + /// + public bool IsAllowed { get; set; } = true; + } +} diff --git a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs index 107dec258..3756872f3 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs @@ -41,6 +41,7 @@ public static void OnMapGenerated() PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.ChaosInsurgency] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.ChaosConscript); PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.OtherAlive] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Tutorial); PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.Dead] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Spectator); + PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.Flamingos] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Flamingo); Timing.CallDelayed(Timing.WaitForOneFrame, Handlers.Map.OnGenerated); } diff --git a/EXILED/Exiled.Events/Handlers/Map.cs b/EXILED/Exiled.Events/Handlers/Map.cs index 3036d4ede..00d73e9ba 100644 --- a/EXILED/Exiled.Events/Handlers/Map.cs +++ b/EXILED/Exiled.Events/Handlers/Map.cs @@ -55,6 +55,11 @@ public static class Map /// public static Event Decontaminating { get; set; } = new(); + /// + /// Invoked after an elevator sequence is updated. + /// + public static Event ElevatorSequencesUpdated { get; set; } = new(); + /// /// Invoked before a grenade explodes. /// @@ -91,12 +96,12 @@ public static class Map public static Event TurningOffLights { get; set; } = new(); /// - /// Invoked after an pickup is spawned. + /// Invoked after a pickup is spawned. /// public static Event PickupAdded { get; set; } = new(); /// - /// Invoked after an pickup is destroyed. + /// Invoked after a pickup is destroyed. /// public static Event PickupDestroyed { get; set; } = new(); @@ -157,6 +162,12 @@ public static class Map /// The instance. public static void OnDecontaminating(DecontaminatingEventArgs ev) => Decontaminating.InvokeSafely(ev); + /// + /// Called after an elevator sequence is updated. + /// + /// The instance. + public static void OnElevatorSequencesUpdated(ElevatorSequencesUpdatedEventArgs ev) => ElevatorSequencesUpdated.InvokeSafely(ev); + /// /// Called before a grenade explodes. /// diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 59519b21e..eda792b61 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -288,6 +288,11 @@ public class Player /// public static Event ReloadingWeapon { get; set; } = new(); + /// + /// Invoked after a reloads a weapon. + /// + public static Event ReloadedWeapon { get; set; } = new(); + /// /// Invoked before spawning a . /// @@ -428,6 +433,11 @@ public class Player /// public static Event UnloadingWeapon { get; set; } = new(); + /// + /// Invoked after a unloads a weapon. + /// + public static Event UnloadedWeapon { get; set; } = new(); + /// /// Invoked before a triggers an aim action. /// @@ -543,6 +553,16 @@ public class Player /// public static Event ChangingNickname { get; set; } = new(); + /// + /// Invoked before a sends valid command. + /// + public static Event SendingValidCommand { get; set; } = new(); + + /// + /// Invoked after a sends valid command. + /// + public static Event SentValidCommand { get; set; } = new(); + /// /// Invoked before a player's emotion changed. /// @@ -563,6 +583,11 @@ public class Player /// public static Event ChangingDisruptorMode { get; set; } = new(); + /// + /// Invoked before player interacts with coffee cup. + /// + public static Event DrinkingCoffee { get; set; } = new(); + /// /// Called before a player's emotion changed. /// @@ -822,6 +847,12 @@ public class Player /// The instance. public static void OnReloadingWeapon(ReloadingWeaponEventArgs ev) => ReloadingWeapon.InvokeSafely(ev); + /// + /// Called after a reloads a weapon. + /// + /// The instance. + public static void OnReloadedWeapon(ReloadedWeaponEventArgs ev) => ReloadedWeapon.InvokeSafely(ev); + /// /// Called before spawning a . /// @@ -924,6 +955,12 @@ public class Player /// The instance. public static void OnUnloadingWeapon(UnloadingWeaponEventArgs ev) => UnloadingWeapon.InvokeSafely(ev); + /// + /// Called after a unloads a weapon. + /// + /// The instance. + public static void OnUnloadedWeapon(UnloadedWeaponEventArgs ev) => UnloadedWeapon.InvokeSafely(ev); + /// /// Called before a triggers an aim action. /// @@ -1198,6 +1235,18 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// The instance. public static void OnChangingNickname(ChangingNicknameEventArgs ev) => ChangingNickname.InvokeSafely(ev); + /// + /// Called before a sends valid command. + /// + /// The instance. + public static void OnSendingValidCommand(SendingValidCommandEventArgs ev) => SendingValidCommand.InvokeSafely(ev); + + /// + /// Called after a sends valid command. + /// + /// The instance. + public static void OnSentValidCommand(SentValidCommandEventArgs ev) => SentValidCommand.InvokeSafely(ev); + /// /// Called before a 's rotates the revolver. /// @@ -1211,32 +1260,15 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item public static void OnChangingDisruptorMode(ChangingDisruptorModeEventArgs ev) => ChangingDisruptorMode.InvokeSafely(ev); /// - /// Called before pre-authenticating a . + /// Called before player interacts with coffee cup. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Returns the instance. - [PluginEvent(ServerEventType.PlayerPreauth)] - public PreauthCancellationData OnPreAuthenticating( - string userId, - string ipAddress, - long expiration, - CentralAuthPreauthFlags flags, - string country, - byte[] signature, - LiteNetLib.ConnectionRequest request, - int readerStartPosition) - { - PreAuthenticatingEventArgs ev = new(userId, ipAddress, expiration, flags, country, signature, request, readerStartPosition); - PreAuthenticating.InvokeSafely(ev); + /// The instance. + public static void OnDrinkingCoffee(DrinkingCoffeeEventArgs ev) => DrinkingCoffee.InvokeSafely(ev); - return ev.CachedPreauthData; - } + /// + /// Called before pre-authenticating a . + /// + /// instance. + public static void OnPreAuthenticating(PreAuthenticatingEventArgs ev) => PreAuthenticating.InvokeSafely(ev); } } diff --git a/EXILED/Exiled.Events/Handlers/Scp079.cs b/EXILED/Exiled.Events/Handlers/Scp079.cs index dab2d32c9..a43016314 100644 --- a/EXILED/Exiled.Events/Handlers/Scp079.cs +++ b/EXILED/Exiled.Events/Handlers/Scp079.cs @@ -82,6 +82,16 @@ public static class Scp079 /// public static Event ZoneBlackout { get; set; } = new(); + /// + /// Invoked before SCP-079 loses a signal by SCP-2176. + /// + public static Event LosingSignal { get; set; } = new(); + + /// + /// Invoked after SCP-079 loses a signal by SCP-2176. + /// + public static Event LostSignal { get; set; } = new(); + /// /// Called before SCP-079 switches cameras. /// @@ -159,5 +169,17 @@ public static class Scp079 /// /// The instance. public static void OnZoneBlackout(ZoneBlackoutEventArgs ev) => ZoneBlackout.InvokeSafely(ev); + + /// + /// Called before SCP-079 loses a signal by SCP-2176. + /// + /// The instance. + public static void OnLosingSignal(LosingSignalEventArgs ev) => LosingSignal.InvokeSafely(ev); + + /// + /// Called after SCP-079 loses a signal by SCP-2176. + /// + /// The instance. + public static void OnLostSignal(LostSignalEventArgs ev) => LostSignal.InvokeSafely(ev); } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp1507.cs b/EXILED/Exiled.Events/Handlers/Scp1507.cs new file mode 100644 index 000000000..63e4536d2 --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Scp1507.cs @@ -0,0 +1,67 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers +{ + using System; + + using Exiled.Events.EventArgs.Scp1507; + using Exiled.Events.Features; + +#pragma warning disable SA1623 + + /// + /// SCP-1507 related events. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public static class Scp1507 + { + /// + /// Invokes before SCP-1507 attacks door. + /// + public static Event AttackingDoor { get; set; } = new(); + + /// + /// Invoked before SCP-1507 screams. + /// + public static Event Screaming { get; set; } = new(); + + /// + /// Invoked before flamingos get spawned. + /// + public static Event SpawningFlamingos { get; set; } = new(); + + /// + /// Invoked before tape is used. + /// + public static Event UsingTape { get; set; } = new(); + + /// + /// Called before SCP-1507 attacks door. + /// + /// The instance. + public static void OnAttackingDoor(AttackingDoorEventArgs ev) => AttackingDoor.InvokeSafely(ev); + + /// + /// Called before SCP-1507 screams. + /// + /// The instance. + public static void OnScreaming(ScreamingEventArgs ev) => Screaming.InvokeSafely(ev); + + /// + /// Called before flamingos get spawned. + /// + /// The instance. + public static void OnSpawningFlamingos(SpawningFlamingosEventArgs ev) => SpawningFlamingos.InvokeSafely(ev); + + /// + /// Called before tape is used. + /// + /// The instance. + public static void OnUsingTape(UsingTapeEventArgs ev) => UsingTape.InvokeSafely(ev); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp2536.cs b/EXILED/Exiled.Events/Handlers/Scp2536.cs new file mode 100644 index 000000000..3f7dfd1c5 --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Scp2536.cs @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers +{ + using Exiled.Events.EventArgs.Scp2536; + using Exiled.Events.Features; +#pragma warning disable SA1623 + + /// + /// SCP-2536 related events. + /// + public static class Scp2536 + { + /// + /// Invoked before SCP-2536 chooses target to spawn. + /// + public static Event FindingPosition { get; set; } = new(); + + /// + /// Invoked before SCP-2536 gives a gift to a player. + /// + public static Event GrantingGift { get; set; } = new(); + + /// + /// Invoked before SCP-2536 is open to a player. + /// + public static Event OpeningGift { get; set; } = new(); + + /// + /// Called before SCP-2536 chooses a target. + /// + /// The instance. + public static void OnFindingPosition(FindingPositionEventArgs ev) => FindingPosition.InvokeSafely(ev); + + /// + /// Called before SCP-2536 gives a gift to a player. + /// + /// The instance. + public static void OnGrantingGift(GrantingGiftEventArgs ev) => GrantingGift.InvokeSafely(ev); + + /// + /// Called before SCP-2536 is open to a player. + /// + /// The instance. + public static void OnOppeningGift(OpeningGiftEventArgs ev) => OpeningGift.InvokeSafely(ev); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp559.cs b/EXILED/Exiled.Events/Handlers/Scp559.cs new file mode 100644 index 000000000..b17f520c9 --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Scp559.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers +{ + using System; + + using Exiled.Events.EventArgs.Scp559; + using Exiled.Events.Features; + +#pragma warning disable SA1623 + + /// + /// All SCP-559 related events. + /// + [Obsolete("Only availaible for Christmas and AprilFools.")] + public static class Scp559 + { + /// + /// Invoked before SCP-559 spawns. + /// + public static Event Spawning { get; set; } = new(); + + /// + /// Invoked before player interacts with SCP-559. + /// + public static Event Interacting { get; set; } = new(); + + /// + /// Called before SCP-559 spawns. + /// + /// The instance. + public static void OnSpawning(SpawningEventArgs ev) => Spawning.InvokeSafely(ev); + + /// + /// Called before player interacts with SCP-559. + /// + /// The instance. + public static void OnInteracting(InteractingScp559EventArgs ev) => Interacting.InvokeSafely(ev); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp939.cs b/EXILED/Exiled.Events/Handlers/Scp939.cs index 04e4997dd..7ccda5f61 100644 --- a/EXILED/Exiled.Events/Handlers/Scp939.cs +++ b/EXILED/Exiled.Events/Handlers/Scp939.cs @@ -8,6 +8,7 @@ namespace Exiled.Events.Handlers { #pragma warning disable SA1623 // Property summary documentation should match accessors + using Exiled.API.Features.Hazards; using Exiled.Events.EventArgs.Scp939; using Exiled.Events.Features; @@ -37,6 +38,11 @@ public static class Scp939 /// public static Event PlacedAmnesticCloud { get; set; } = new(); + /// + /// Invoked after SCP-939 fully spawned . + /// + public static Event CreatedAmnesticCloud { get; set; } = new(); + /// /// Invoked before SCP-939 plays a stolen voice. /// @@ -92,6 +98,12 @@ public static class Scp939 /// The instance. public static void OnPlacedAmnesticCloud(PlacedAmnesticCloudEventArgs ev) => PlacedAmnesticCloud.InvokeSafely(ev); + /// + /// Called after SCP-939 fully spawned . + /// + /// The instance. + public static void OnCreatedAmnesticCloud(CreatedAmnesticCloudEventArgs ev) => CreatedAmnesticCloud.InvokeSafely(ev); + /// /// Called before SCP-939 plays a stolen voice. /// diff --git a/EXILED/Exiled.Events/Handlers/Server.cs b/EXILED/Exiled.Events/Handlers/Server.cs index c86a8adee..7ddffb003 100644 --- a/EXILED/Exiled.Events/Handlers/Server.cs +++ b/EXILED/Exiled.Events/Handlers/Server.cs @@ -122,6 +122,11 @@ public static class Server /// public static Event Unbanned { get; set; } = new(); + /// + /// Invoked before before the completion of an objective. + /// + public static Event CompletingObjective { get; set; } = new(); + /// /// Called before waiting for players. /// @@ -233,5 +238,11 @@ public static class Server /// /// The instance. public static void OnUnbanned(UnbannedEventArgs ev) => Unbanned.InvokeSafely(ev); + + /// + /// Called before the completion of an objective. + /// + /// The instance. + public static void OnCompletingObjective(CompletingObjectiveEventArgs ev) => CompletingObjective.InvokeSafely(ev); } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Warhead.cs b/EXILED/Exiled.Events/Handlers/Warhead.cs index 0d253e121..1656f3a4a 100644 --- a/EXILED/Exiled.Events/Handlers/Warhead.cs +++ b/EXILED/Exiled.Events/Handlers/Warhead.cs @@ -37,6 +37,11 @@ public class Warhead /// public static Event Detonated { get; set; } = new(); + /// + /// Invoked before the DeadmanSwitch is Initiated. + /// + public static Event DeadmanSwitchInitiating { get; set; } = new(); + /// /// Invoked before detonating the warhead. /// @@ -65,6 +70,12 @@ public class Warhead /// public static void OnDetonated() => Detonated.InvokeSafely(); + /// + /// Called after the warhead has been detonated. + /// + /// The instance. + public static void OnDeadmanSwitchInitiating(DeadmanSwitchInitiatingEventArgs ev) => DeadmanSwitchInitiating.InvokeSafely(ev); + /// /// Called before detonating the warhead. /// 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/ElevatorSequencesUpdated.cs b/EXILED/Exiled.Events/Patches/Events/Map/ElevatorSequencesUpdated.cs new file mode 100644 index 000000000..098a5852a --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Map/ElevatorSequencesUpdated.cs @@ -0,0 +1,31 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Map +{ + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Map; + using HarmonyLib; + using Interactables.Interobjects; + + /// + /// Patches 's setter. + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.ElevatorSequencesUpdated))] + [HarmonyPatch(typeof(ElevatorChamber), nameof(ElevatorChamber.CurSequence), MethodType.Setter)] + internal class ElevatorSequencesUpdated + { +#pragma warning disable SA1313 + private static void Postfix(ElevatorChamber __instance) +#pragma warning restore SA1313 + { + ElevatorSequencesUpdatedEventArgs ev = new(__instance, __instance.CurSequence); + Handlers.Map.OnElevatorSequencesUpdated(ev); + } + } +} \ No newline at end of file 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 +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Player +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.DrinkingCoffee))] + [HarmonyPatch(typeof(global::Coffee), nameof(global::Coffee.ServerInteract))] + internal class DrinkingCoffee + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ret) + offset; + + Label retLabel = generator.DefineLabel(); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + newInstructions.InsertRange( + index, + new[] + { + // Player.Get(hub); + new CodeInstruction(OpCodes.Ldarg_1).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // Coffee.Get(this) + new(OpCodes.Ldarg_0), + new(OpCodes.Call, Method(typeof(Coffee), nameof(Coffee.Get), new[] { typeof(global::Coffee) })), + + // true + new(OpCodes.Ldc_I4_1), + + // DrinkingCoffeeEventArgs ev = new(Player, Coffee, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DrinkingCoffeeEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Player.OnDrinkingCoffee(ev); + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnDrinkingCoffee))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(DrinkingCoffeeEventArgs), nameof(DrinkingCoffeeEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs b/EXILED/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs index 63b5e620f..180a0bd11 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs @@ -50,9 +50,8 @@ private static 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/FirearmRequestReceived.cs b/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs deleted file mode 100644 index 7da404755..000000000 --- a/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs +++ /dev/null @@ -1,274 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Patches.Events.Player -{ - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Reflection.Emit; - - using API.Features.Pools; - using Exiled.API.Features.Items; - using Exiled.Events.Attributes; - using Exiled.Events.EventArgs.Player; - using Handlers; - using HarmonyLib; - using InventorySystem.Items; - using InventorySystem.Items.Firearms; - using InventorySystem.Items.Firearms.BasicMessages; - using PluginAPI.Events; - - using static HarmonyLib.AccessTools; - - /* TODO - /// - /// Patches . - /// Adds , , - /// , and - /// events. - /// - [EventPatch(typeof(Player), nameof(Player.ReloadingWeapon))] - [EventPatch(typeof(Player), nameof(Player.UnloadingWeapon))] - [EventPatch(typeof(Player), nameof(Player.DryfiringWeapon))] - [EventPatch(typeof(Player), nameof(Player.AimingDownSight))] - [EventPatch(typeof(Player), nameof(Player.TogglingWeaponFlashlight))] - [HarmonyPatch(typeof(FirearmUtils), nameof())] - internal static class FirearmRequestReceived - { - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) - { - List newInstructions = ListPool.Pool.Get(instructions); - - LocalBuilder ev = generator.DeclareLocal(typeof(TogglingWeaponFlashlightEventArgs)); - LocalBuilder player = generator.DeclareLocal(typeof(API.Features.Player)); - LocalBuilder firearm = generator.DeclareLocal(typeof(Firearm)); - - Label returnLabel = generator.DefineLabel(); - - int offset = -1; - int index = newInstructions.FindLastIndex(instruction => instruction.LoadsField(Field(typeof(RequestMessage), nameof(RequestMessage.Request)))) + offset; - - newInstructions.InsertRange(index, new[] - { - // Player player = Player.Get(hub); - // if (player == null) - // return; - new CodeInstruction(OpCodes.Ldloc_0).MoveLabelsFrom(newInstructions[index]), - new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), - new(OpCodes.Dup), - new(OpCodes.Stloc_S, player.LocalIndex), - new(OpCodes.Brfalse_S, returnLabel), - - // Firearm firearm = (Firearm)Item.Get(hub); - // if (Firearm == null) - // return; - new CodeInstruction(OpCodes.Ldloc_1), - new(OpCodes.Call, GetDeclaredMethods(typeof(API.Features.Items.Item)).First(x => !x.IsGenericMethod && x.Name is nameof(API.Features.Items.Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))), - new(OpCodes.Isinst, typeof(Firearm)), - new(OpCodes.Dup), - new(OpCodes.Stloc_S, firearm.LocalIndex), - new(OpCodes.Brfalse_S, returnLabel), - }); - - offset = -2; - index = newInstructions.FindIndex( - instruction => instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == GetDeclaredConstructors(typeof(PlayerReloadWeaponEvent))[0]) + offset; - - newInstructions.InsertRange( - index, - new[] - { - // player - new CodeInstruction(OpCodes.Ldloc_S, player.LocalIndex).MoveLabelsFrom(newInstructions[index]), - - // firearm - new(OpCodes.Ldloc_S, firearm.LocalIndex), - - // true - new(OpCodes.Ldc_I4_1), - - // ReloadingWeaponEventArgs ev = new(Player, firearm, bool) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ReloadingWeaponEventArgs))[0]), - new(OpCodes.Dup), - - // Player.OnReloadingWeapon(ev) - new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnReloadingWeapon))), - - // if (!ev.IsAllowed) - // return; - new(OpCodes.Callvirt, PropertyGetter(typeof(ReloadingWeaponEventArgs), nameof(ReloadingWeaponEventArgs.IsAllowed))), - new(OpCodes.Brfalse, returnLabel), - }); - - offset = -2; - index = newInstructions.FindIndex( - instruction => instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == GetDeclaredConstructors(typeof(PlayerUnloadWeaponEvent))[0]) + offset; - - newInstructions.InsertRange( - index, - new[] - { - // player - new CodeInstruction(OpCodes.Ldloc_S, player.LocalIndex).MoveLabelsFrom(newInstructions[index]), - - // firearm - new(OpCodes.Ldloc_S, firearm.LocalIndex), - - // true - new(OpCodes.Ldc_I4_1), - - // UnloadingWeaponEventArgs ev = new(Player, firearm, bool) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnloadingWeaponEventArgs))[0]), - new(OpCodes.Dup), - - // Player.OnUnloadingWeapon(ev) - new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnUnloadingWeapon))), - - // if (!ev.IsAllowed) - // return; - new(OpCodes.Callvirt, PropertyGetter(typeof(UnloadingWeaponEventArgs), nameof(UnloadingWeaponEventArgs.IsAllowed))), - new(OpCodes.Brfalse, returnLabel), - }); - - offset = -2; - index = newInstructions.FindIndex( - instruction => instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == GetDeclaredConstructors(typeof(PlayerDryfireWeaponEvent))[0]) + offset; - - newInstructions.InsertRange( - index, - new[] - { - // player - new CodeInstruction(OpCodes.Ldloc_S, player.LocalIndex).MoveLabelsFrom(newInstructions[index]), - - // firearm - new(OpCodes.Ldloc_S, firearm.LocalIndex), - - // true - new(OpCodes.Ldc_I4_1), - - // DryfiringWeaponEventArgs ev = new(Player, firearm, bool) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DryfiringWeaponEventArgs))[0]), - new(OpCodes.Dup), - - // Player.OnDryfiringWeapon(ev) - new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnDryfiringWeapon))), - - // if (!ev.IsAllowed) - // return; - new(OpCodes.Callvirt, PropertyGetter(typeof(DryfiringWeaponEventArgs), nameof(DryfiringWeaponEventArgs.IsAllowed))), - new(OpCodes.Brfalse, returnLabel), - }); - - offset = -3; - index = newInstructions.FindIndex( - instruction => instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == GetDeclaredConstructors(typeof(PlayerAimWeaponEvent))[0]) + offset; - - newInstructions.InsertRange( - index, - new[] - { - // player - new CodeInstruction(OpCodes.Ldloc_S, player.LocalIndex).MoveLabelsFrom(newInstructions[index]), - - // firearm - new(OpCodes.Ldloc_S, firearm.LocalIndex), - - // true (adsIn) - new(OpCodes.Ldc_I4_1), - - // false (adsOut) - new(OpCodes.Ldc_I4_0), - - // AimingDownSightEventArgs ev = new(Player, firearm, bool, bool) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AimingDownSightEventArgs))[0]), - - // Player.OnAimingDownSight(ev) - new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnAimingDownSight))), - }); - - offset = -3; - index = newInstructions.FindLastIndex( - instruction => instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == GetDeclaredConstructors(typeof(PlayerAimWeaponEvent))[0]) + offset; - - newInstructions.InsertRange( - index, - new[] - { - // player - new CodeInstruction(OpCodes.Ldloc_S, player.LocalIndex).MoveLabelsFrom(newInstructions[index]), - - // firearm - new(OpCodes.Ldloc_S, firearm.LocalIndex), - - // false (adsIn) - new(OpCodes.Ldc_I4_0), - - // true (adsOut) - new(OpCodes.Ldc_I4_1), - - // AimingDownSightEventArgs ev = new(Player, firearm, bool, bool) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AimingDownSightEventArgs))[0]), - - // Player.OnAimingDownSight(ev) - new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnAimingDownSight))), - }); - - offset = -7; - index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ceq) + offset; - - newInstructions.InsertRange( - index, - new[] - { - // player - new CodeInstruction(OpCodes.Ldloc_S, player.LocalIndex), - - // firearm - new(OpCodes.Ldloc_S, firearm.LocalIndex), - - // !flag - new(OpCodes.Ldloc_S, 8), - new(OpCodes.Ldc_I4_0), - new(OpCodes.Ceq), - - // true - new(OpCodes.Ldc_I4_1), - - // TogglingWeaponFlashlightEventArgs ev = new(Player, firearm, bool, bool) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(TogglingWeaponFlashlightEventArgs))[0]), - new(OpCodes.Dup), - new(OpCodes.Dup), - new(OpCodes.Stloc_S, ev.LocalIndex), - - // Player.OnTogglingWeaponFlashlight(ev) - new(OpCodes.Call, Method(typeof(Player), nameof(Player.OnTogglingWeaponFlashlight))), - - // if (!ev.IsAllowed) - // return; - new(OpCodes.Callvirt, PropertyGetter(typeof(TogglingWeaponFlashlightEventArgs), nameof(TogglingWeaponFlashlightEventArgs.IsAllowed))), - new(OpCodes.Brfalse_S, returnLabel), - - // flag = !ev.NewState - new(OpCodes.Ldloc_S, ev.LocalIndex), - new(OpCodes.Callvirt, PropertyGetter(typeof(TogglingWeaponFlashlightEventArgs), nameof(TogglingWeaponFlashlightEventArgs.NewState))), - new(OpCodes.Ldc_I4_0), - new(OpCodes.Ceq), - new(OpCodes.Stloc_S, 6), - }); - - newInstructions[newInstructions.Count - 1].WithLabels(returnLabel); - - for (int z = 0; z < newInstructions.Count; z++) - yield return newInstructions[z]; - - ListPool.Pool.Return(newInstructions); - } - } - */ -} \ No newline at end of file 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 +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Player +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + + using HarmonyLib; + + using Hazards; + using LiteNetLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.PreAuthenticating))] + [HarmonyPatch(typeof(CustomLiteNetLib4MirrorTransport), nameof(CustomLiteNetLib4MirrorTransport.ProcessConnectionRequest))] + internal static class PreAuthenticating + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label ret = generator.DefineLabel(); + newInstructions[newInstructions.Count - 1].labels.Add(ret); + LocalBuilder ev = generator.DeclareLocal(typeof(PreAuthenticatingEventArgs)); + int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldstr && instruction.operand == (object)"{0};{1};{2};{3}"); + + Label cont = generator.DefineLabel(); + newInstructions[index].labels.Add(cont); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // userid + new CodeInstruction(OpCodes.Ldloc_S, 10), + + // ipaddress + new (OpCodes.Ldloc_S, 15), + + // expiration + new (OpCodes.Ldloc_S, 11), + + // flags + new (OpCodes.Ldloc_S, 12), + + // country + new (OpCodes.Ldloc_S, 13), + + // signature + new (OpCodes.Ldloc_S, 14), + + // request + new (OpCodes.Ldarg_1), + + // position + new (OpCodes.Ldloc_S, 9), + + // PreAuthenticatingEventArgs ev = new (userid, ipaddress, expiration, flags, country, signature, request, position) + new (OpCodes.Newobj, GetDeclaredConstructors(typeof(PreAuthenticatingEventArgs))[0]), + new (OpCodes.Dup), + new (OpCodes.Stloc_S, ev.LocalIndex), + + // OnPreAuthenticating(ev) + new (OpCodes.Call, AccessTools.Method(typeof(Handlers.Player), nameof(Handlers.Player.OnPreAuthenticating))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ReloadedAndUnloaded.cs b/EXILED/Exiled.Events/Patches/Events/Player/ReloadedAndUnloaded.cs new file mode 100644 index 000000000..96152eafb --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/ReloadedAndUnloaded.cs @@ -0,0 +1,100 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Player +{ +#pragma warning disable SA1313 // Parameter names should begin with lower-case letter + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + + using HarmonyLib; + using InventorySystem.Items.Autosync; + using InventorySystem.Items.Firearms; + using InventorySystem.Items.Firearms.Modules; + using InventorySystem.Items.Firearms.Modules.Misc; + using InventorySystem.Searching; + using Mirror; + using UnityEngine; + + using static HarmonyLib.AccessTools; + + using Player = API.Features.Player; + + /// + /// Patches to add missing event handler to the + /// and . + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.UnloadedWeapon))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ReloadedWeapon))] + [HarmonyPatch(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.StopReloadingAndUnloading))] + internal static class ReloadedAndUnloaded + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label skipLabel = generator.DefineLabel(); + Label retLabel = generator.DefineLabel(); + LocalBuilder isReloaded = generator.DeclareLocal(typeof(bool)); + LocalBuilder isUnloaded = generator.DeclareLocal(typeof(bool)); + newInstructions.InsertRange(0, new CodeInstruction[] + { + // bool isReloaded = __instance.IsReloading; + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.IsReloading))), + new(OpCodes.Stloc_S, isReloaded.LocalIndex), + + // bool isUnloaded = __instance.IsUnloading; + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.IsUnloading))), + new(OpCodes.Stloc_S, isUnloaded.LocalIndex), + }); + + newInstructions.InsertRange(newInstructions.Count - 1, new[] + { + // if (!isReloaded) goto skipLabel; + new(OpCodes.Ldloc_S, isReloaded.LocalIndex), + new(OpCodes.Brfalse_S, skipLabel), + + // __instance.Firearm + new CodeInstruction(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.Firearm))), + + // ReloadedWeaponEventArgs evReloadedWeapon = new(Firearm) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ReloadedWeaponEventArgs))[0]), + + // Handlers.Player.OnReloadedWeapon(evReloadedWeapon) + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnReloadedWeapon))), + + // if (!isUnloaded) return; + new CodeInstruction(OpCodes.Ldloc_S, isUnloaded.LocalIndex).WithLabels(skipLabel), + new(OpCodes.Brfalse_S, retLabel), + + // __instance.Firearm + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.Firearm))), + + // UnloadedWeaponEventArgs evUnloadedWeaponE = new(Firearm) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnloadedWeaponEventArgs))[0]), + + // Handlers.Player.OnUnloadedWeapon(evUnloadedWeaponE) + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnUnloadedWeapon))), + }); + + newInstructions[newInstructions.Count - 1].WithLabels(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ReloadingWeapon.cs b/EXILED/Exiled.Events/Patches/Events/Player/ReloadingWeapon.cs index fea606842..347e82bad 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/ReloadingWeapon.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/ReloadingWeapon.cs @@ -22,9 +22,10 @@ namespace Exiled.Events.Patches.Events.Player /// /// Patches . - /// Adds the event. + /// Adds the and event. /// [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ReloadingWeapon))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.UnloadingWeapon))] [HarmonyPatch(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.ServerProcessCmd))] internal static class ReloadingWeapon { @@ -32,16 +33,17 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - int offset = 2; + int offset = -2; int index = newInstructions.FindIndex(x => x.Calls(Method(typeof(IReloadUnloadValidatorModule), nameof(IReloadUnloadValidatorModule.ValidateReload)))) + offset; - Label skip = (Label)newInstructions[index - 1].operand; + Label stopMessage = generator.DefineLabel(); + newInstructions.InsertRange( index, new[] { - // player - new CodeInstruction(OpCodes.Ldarg_0), + // this.Firearm + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.Firearm))), // ReloadingWeaponEventArgs ev = new(firearm) @@ -52,11 +54,46 @@ private static IEnumerable Transpiler(IEnumerable + // { + // x.WriteSubheader(AnimatorReloaderModuleBase.ReloaderMessageHeader.Stop); + // }, true); new(OpCodes.Callvirt, PropertyGetter(typeof(ReloadingWeaponEventArgs), nameof(ReloadingWeaponEventArgs.IsAllowed))), - new(OpCodes.Brfalse, skip), + new(OpCodes.Brfalse, stopMessage), + }); + + offset = -2; + index = newInstructions.FindIndex(x => x.Calls(Method(typeof(IReloadUnloadValidatorModule), nameof(IReloadUnloadValidatorModule.ValidateUnload)))) + offset; + + newInstructions.InsertRange( + index, + new[] + { + // this.Firearm + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.Firearm))), + + // UnloadingWeaponEventArgs ev = new(firearm) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnloadingWeaponEventArgs))[0]), + new(OpCodes.Dup), + + // Player.OnUnloadingWeapon(ev) + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnUnloadingWeapon))), + + // if (!ev.IsAllowed) + // this.SendRpc(NetworkWriter x => + // { + // x.WriteSubheader(AnimatorReloaderModuleBase.ReloaderMessageHeader.Stop); + // }, true); + new (OpCodes.Callvirt, PropertyGetter(typeof(UnloadingWeaponEventArgs), nameof(UnloadingWeaponEventArgs.IsAllowed))), + new(OpCodes.Brfalse, stopMessage), }); + offset = 2; + index = newInstructions.FindIndex(x => x.operand == (object)PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.IsUnloading))) + offset; + + newInstructions[index].labels.Add(stopMessage); + for (int z = 0; z < newInstructions.Count; z++) yield return newInstructions[z]; diff --git a/EXILED/Exiled.Events/Patches/Events/Player/SendingValidGameConsoleCommand.cs b/EXILED/Exiled.Events/Patches/Events/Player/SendingValidGameConsoleCommand.cs new file mode 100644 index 000000000..a8a4d7027 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/SendingValidGameConsoleCommand.cs @@ -0,0 +1,178 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Player +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features; + using API.Features.Pools; + using CommandSystem; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + + using HarmonyLib; + + using RemoteAdmin; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the and + /// the events. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.SendingValidCommand))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.SentValidCommand))] + [HarmonyPatch(typeof(QueryProcessor), nameof(QueryProcessor.ProcessGameConsoleQuery))] + internal static class SendingValidGameConsoleCommand + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label ret = generator.DefineLabel(); + newInstructions[newInstructions.Count - 1].WithLabels(ret); + + Label setproperresp = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(SendingValidCommandEventArgs)); + + int offset = 2; + int index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(ClientCommandHandler), nameof(ClientCommandHandler.TryGetCommand)))) + offset; + Label contlabel = generator.DefineLabel(); + + offset = -6; + int sendreplyidx = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldstr && (string)instruction.operand == "magenta") + offset; + Label sendreply = generator.DefineLabel(); + newInstructions[sendreplyidx].WithLabels(sendreply); + + newInstructions[index].WithLabels(contlabel); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // this + new CodeInstruction(OpCodes.Ldarg_0), + + // this._hub + new CodeInstruction(OpCodes.Ldfld, Field(typeof(QueryProcessor), nameof(QueryProcessor._hub))), + + // Player.Get(Hub) + new CodeInstruction(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new Type[] { typeof(ReferenceHub) })), + + // command + new (OpCodes.Ldloc_S, 1), + + // commandtype client + new CodeInstruction(OpCodes.Ldc_I4_2), + + // query + new CodeInstruction(OpCodes.Ldarg_1), + + // response + new CodeInstruction(OpCodes.Ldloc_S, 3), + + // new SendingValidCommandEventArgs + new CodeInstruction(OpCodes.Newobj, GetDeclaredConstructors(typeof(SendingValidCommandEventArgs))[0]), + new CodeInstruction(OpCodes.Dup), + new CodeInstruction(OpCodes.Stloc_S, ev.LocalIndex), + + // OnSendingValidCommad(ev) + new CodeInstruction(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSendingValidCommand))), + + // if ev.IsAllowed cont + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.IsAllowed))), + new CodeInstruction(OpCodes.Brtrue_S, contlabel), + + // if ev.Response.IsNullOrEmpty rets + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new CodeInstruction(OpCodes.Call, Method(typeof(string), nameof(string.IsNullOrEmpty))), + new CodeInstruction(OpCodes.Brtrue_S, setproperresp), + + // response = ev.Response + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new CodeInstruction(OpCodes.Stloc_S, 3), + + // goto sendreply + new CodeInstruction(OpCodes.Br, sendreply), + + // response = "The Command Execution Was Prevented By Plugin." + new CodeInstruction(OpCodes.Ldstr, "The Command Execution Was Prevented By Plugin.").WithLabels(setproperresp), + new CodeInstruction(OpCodes.Stloc_S, 3), + new CodeInstruction(OpCodes.Br, sendreply), + }); + + offset = -3; + index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldstr && (string)instruction.operand == "magenta") + offset; + Label skip = generator.DefineLabel(); + newInstructions[index].WithLabels(skip); + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // if ev.Response.IsNullOrEmpty skip + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new CodeInstruction(OpCodes.Call, Method(typeof(string), nameof(string.IsNullOrEmpty))), + new CodeInstruction(OpCodes.Brtrue_S, skip), + + // response = ev.Response + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new CodeInstruction(OpCodes.Stloc_S, 3), + }); + offset = 0; + index = newInstructions.FindIndex(instrction => instrction.Calls(Method(typeof(GameConsoleTransmission), nameof(GameConsoleTransmission.SendToClient)))) + offset; + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // this + new CodeInstruction(OpCodes.Ldarg_0), + + // this._hub + new CodeInstruction(OpCodes.Ldfld, Field(typeof(QueryProcessor), nameof(QueryProcessor._hub))), + + // Player.Get(Hub) + new CodeInstruction(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new Type[] { typeof(ReferenceHub) })), + + // command + new CodeInstruction(OpCodes.Ldloc_1), + + // commandtype CLIENT + new CodeInstruction(OpCodes.Ldc_I4_2), + + // query + new CodeInstruction(OpCodes.Ldarg_1), + + // response + new CodeInstruction(OpCodes.Ldloc_S, 3), + + // result + new (OpCodes.Ldloc_S, 2), + + // new SentValidCommandEventArgs + new (OpCodes.Newobj, GetDeclaredConstructors(typeof(SentValidCommandEventArgs))[0]), + + // OnSentValidCommand(ev) + new (OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSentValidCommand))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} diff --git a/EXILED/Exiled.Events/Patches/Events/Player/SendingValidRACommand.cs b/EXILED/Exiled.Events/Patches/Events/Player/SendingValidRACommand.cs new file mode 100644 index 000000000..5e051c8b4 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/SendingValidRACommand.cs @@ -0,0 +1,170 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Player +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features; + using API.Features.Pools; + using CommandSystem; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + + using HarmonyLib; + + using RemoteAdmin; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the and + /// the events. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.SendingValidCommand))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.SentValidCommand))] + [HarmonyPatch(typeof(CommandProcessor), nameof(CommandProcessor.ProcessQuery))] + internal static class SendingValidRACommand + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label setptroperresp = generator.DefineLabel(); + + Label ret = generator.DefineLabel(); + newInstructions[newInstructions.Count - 1].WithLabels(ret); + LocalBuilder ev = generator.DeclareLocal(typeof(SendingValidCommandEventArgs)); + int offset = 2; + int index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(CommandHandler), nameof(CommandHandler.TryGetCommand)))) + offset; + + Label contlabel = generator.DefineLabel(); + newInstructions[index].WithLabels(contlabel); + + int sendreplyidx = newInstructions.FindIndex(instructions => instructions.Calls(Method(typeof(string), nameof(string.IsNullOrEmpty)))) + offset; + + Label sendreply = generator.DefineLabel(); + newInstructions[sendreplyidx].WithLabels(sendreply); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // sender + new (OpCodes.Ldarg_1), + + // Player.get(sender) + new (OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new Type[] { typeof(CommandSender) })), + + // command + new (OpCodes.Ldloc_1), + + // commandtype + new (OpCodes.Ldc_I4_4), + + // query + new (OpCodes.Ldarg_0), + + // response + new (OpCodes.Ldloc_S, 6), + + // new SendingValidCommandEventArgs + new (OpCodes.Newobj, GetDeclaredConstructors(typeof(SendingValidCommandEventArgs))[0]), + new (OpCodes.Dup), + new (OpCodes.Stloc_S, ev.LocalIndex), + + // OnSendingValidCommad(ev) + new (OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSendingValidCommand))), + + // if ev.IsAllowed cont + new (OpCodes.Ldloc_S, ev.LocalIndex), + new (OpCodes.Callvirt, PropertyGetter(typeof(SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.IsAllowed))), + new (OpCodes.Brtrue_S, contlabel), + + // if ev.Response.IsNullOrEmpty rets + new (OpCodes.Ldloc_S, ev.LocalIndex), + new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new (OpCodes.Call, Method(typeof(string), nameof(string.IsNullOrEmpty))), + new (OpCodes.Brtrue_S, setptroperresp), + + // response = ev.Response + new (OpCodes.Ldloc_S, ev.LocalIndex), + new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new (OpCodes.Stloc_S, 6), + + // goto sendreply + new (OpCodes.Br, sendreply), + + // response = "The Command Execution Was Prevented By Plugin." + new CodeInstruction(OpCodes.Ldstr, "The Command Execution Was Prevented By Plugin.").WithLabels(setptroperresp), + new (OpCodes.Stloc_S, 6), + new (OpCodes.Br, sendreply), + }); + offset = -4; + index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(string), nameof(string.ToUpperInvariant)))) + offset; + Label skip = generator.DefineLabel(); + newInstructions[index].WithLabels(skip); + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // if ev.Response.IsNullOrEmpty skip + new (OpCodes.Ldloc_S, ev.LocalIndex), + new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new (OpCodes.Call, Method(typeof(string), nameof(string.IsNullOrEmpty))), + new (OpCodes.Brtrue_S, skip), + + // response = ev.Response + new (OpCodes.Ldloc_S, ev.LocalIndex), + new (OpCodes.Callvirt, PropertyGetter(typeof (SendingValidCommandEventArgs), nameof(SendingValidCommandEventArgs.Response))), + new (OpCodes.Stloc_S, 6), + }); + + offset = 0; + index = newInstructions.FindIndex(instrction => instrction.Calls(Method(typeof(CommandSender), nameof(CommandSender.RaReply)))) + offset; + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // sender + new (OpCodes.Ldarg_1), + + // Player.get(sender) + new (OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new Type[] { typeof(CommandSender) })), + + // command + new (OpCodes.Ldloc_1), + + // commandtype + new (OpCodes.Ldc_I4_4), + + // query + new (OpCodes.Ldarg_0), + + // response + new (OpCodes.Ldloc_S, 6), + + // result + new (OpCodes.Ldloc_S, 5), + + // new SentValidCommandEventArgs + new (OpCodes.Newobj, GetDeclaredConstructors(typeof(SentValidCommandEventArgs))[0]), + + // OnSentValidCommand(ev) + new (OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSentValidCommand))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} diff --git a/EXILED/Exiled.Events/Patches/Events/Player/UnloadingWeapon.cs b/EXILED/Exiled.Events/Patches/Events/Player/UnloadingWeapon.cs deleted file mode 100644 index 3a7589489..000000000 --- a/EXILED/Exiled.Events/Patches/Events/Player/UnloadingWeapon.cs +++ /dev/null @@ -1,66 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Patches.Events.Player -{ - using System.Collections.Generic; - using System.Reflection.Emit; - - using API.Features.Pools; - - using Exiled.API.Extensions; - using Exiled.Events.Attributes; - using Exiled.Events.EventArgs.Player; - using HarmonyLib; - using InventorySystem.Items.Firearms.Modules; - - using static HarmonyLib.AccessTools; - - /// - /// Patches . - /// Adds the event. - /// - [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.UnloadingWeapon))] - [HarmonyPatch(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.ServerProcessCmd))] - internal static class UnloadingWeapon - { - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) - { - List newInstructions = ListPool.Pool.Get(instructions); - - int offset = 2; - int index = newInstructions.FindIndex(x => x.Calls(Method(typeof(IReloadUnloadValidatorModule), nameof(IReloadUnloadValidatorModule.ValidateUnload)))) + offset; - - Label skip = (Label)newInstructions[index - 1].operand; - newInstructions.InsertRange( - index, - new[] - { - // this.Firearm - new CodeInstruction(OpCodes.Ldarg_0), - new(OpCodes.Callvirt, PropertyGetter(typeof(AnimatorReloaderModuleBase), nameof(AnimatorReloaderModuleBase.Firearm))), - - // UnloadingWeaponEventArgs ev = new(firearm) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnloadingWeaponEventArgs))[0]), - new(OpCodes.Dup), - - // Player.OnUnloadingWeapon(ev) - new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnUnloadingWeapon))), - - // if (!ev.IsAllowed) - // goto skip; - new(OpCodes.Callvirt, PropertyGetter(typeof(UnloadingWeaponEventArgs), nameof(UnloadingWeaponEventArgs.IsAllowed))), - new(OpCodes.Brfalse, skip), - }); - - for (int z = 0; z < newInstructions.Count; z++) - yield return newInstructions[z]; - - ListPool.Pool.Return(newInstructions); - } - } -} diff --git a/EXILED/Exiled.Events/Patches/Events/Scp079/Lost.cs b/EXILED/Exiled.Events/Patches/Events/Scp079/Lost.cs new file mode 100644 index 000000000..4583f447f --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp079/Lost.cs @@ -0,0 +1,86 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp079 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp079; + using Exiled.Events.Handlers; + + using HarmonyLib; + + using PlayerRoles; + using PlayerRoles.PlayableScps.Scp079; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the and event. + /// + [EventPatch(typeof(Scp079), nameof(Scp079.LosingSignal))] + [EventPatch(typeof(Scp079), nameof(Scp079.LostSignal))] + [HarmonyPatch(typeof(Scp079LostSignalHandler), nameof(Scp079LostSignalHandler.ServerLoseSignal))] + internal static class Lost + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label ret = generator.DefineLabel(); + + newInstructions.InsertRange( + 0, + new[] + { + // Role._lastOwner + new CodeInstruction(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(Scp079LostSignalHandler), nameof(Scp079LostSignalHandler.Role))), + new(OpCodes.Ldfld, Field(typeof(PlayerRoleBase), nameof(PlayerRoleBase._lastOwner))), + + // LosingSignalEventArgs ev = new(Role._lastOwner) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(LosingSignalEventArgs))[0]), + new(OpCodes.Dup), + + // Scp079.OnLosingSignal(ev) + new(OpCodes.Call, Method(typeof(Scp079), nameof(Scp079.OnLosingSignal))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(LosingSignalEventArgs), nameof(LosingSignalEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, ret), + }); + + newInstructions.InsertRange( + newInstructions.Count - 1, + new[] + { + // Role._lastOwner + new CodeInstruction(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(Scp079LostSignalHandler), nameof(Scp079LostSignalHandler.Role))), + new(OpCodes.Ldfld, Field(typeof(PlayerRoleBase), nameof(PlayerRoleBase._lastOwner))), + + // LostSignalEventArgs ev = new(Role._lastOwner) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(LostSignalEventArgs))[0]), + + // Scp079.OnLosingSignal(ev) + new(OpCodes.Call, Method(typeof(Scp079), nameof(Scp079.OnLostSignal))), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(ret); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs b/EXILED/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs new file mode 100644 index 000000000..76e5dcb1e --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs @@ -0,0 +1,79 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ +#pragma warning disable CS0618 + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp1507; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp1507; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.AttackingDoor))] + [HarmonyPatch(typeof(Scp1507AttackAbility), nameof(Scp1507AttackAbility.TryAttackDoor))] + internal class AttackingDoor + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = -4; + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ldloc_S) + offset; + + Label continueLabel = generator.DefineLabel(); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // Player.Get(this.Owner); + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Callvirt, PropertyGetter(typeof(Scp1507AttackAbility), nameof(Scp1507AttackAbility.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // target1 + new(OpCodes.Ldloc_3), + + // true + new(OpCodes.Ldc_I4_1), + + // AttackingDoorEventArgs ev = new(Player, DoorVariant, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AttackingDoorEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Scp1507.OnAttackingDoor(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.OnAttackingDoor))), + + // if (!ev.IsAllowed) + new(OpCodes.Callvirt, PropertyGetter(typeof(AttackingDoorEventArgs), nameof(AttackingDoorEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + + // return false; + new(OpCodes.Ldc_I4_0), + new(OpCodes.Ret), + + new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1507/Scream.cs b/EXILED/Exiled.Events/Patches/Events/Scp1507/Scream.cs new file mode 100644 index 000000000..cb9a6d3cb --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/Scream.cs @@ -0,0 +1,68 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ +#pragma warning disable CS0618 + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp1507; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp1507; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.Screaming))] + [HarmonyPatch(typeof(Scp1507VocalizeAbility), nameof(Scp1507VocalizeAbility.ServerProcessCmd))] + public class Scream + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + + newInstructions.InsertRange(0, new CodeInstruction[] + { + // Player.Get(this.Owner); + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(Scp1507VocalizeAbility), nameof(Scp1507VocalizeAbility.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // ScreamingEventArgs ev = new(Player, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ScreamingEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Scp1507.OnScreaming(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.OnScreaming))), + + // if (!ev.IsAllowed) + // goto retLabel; + new(OpCodes.Callvirt, PropertyGetter(typeof(ScreamingEventArgs), nameof(ScreamingEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1507/SpawningFlamingos.cs b/EXILED/Exiled.Events/Patches/Events/Scp1507/SpawningFlamingos.cs new file mode 100644 index 000000000..0fbaa80f1 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/SpawningFlamingos.cs @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ +#pragma warning disable CS0618 + using System.Collections.Generic; + using System.Linq; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp1507; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp1507; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.SpawningFlamingos))] + [HarmonyPatch(typeof(Scp1507Spawner), nameof(Scp1507Spawner.Spawn))] + internal class SpawningFlamingos + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(SpawningFlamingosEventArgs)); + + int offset = 0; + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ldc_I4_4) + offset; + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // Player.Get(Scp1507Spawner._alpha); + new(OpCodes.Ldsfld, Field(typeof(Scp1507Spawner), nameof(Scp1507Spawner._alpha))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // SpawningFlamingosEventArgs ev = new(Player, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(SpawningFlamingosEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Scp1507.OnSpawningFlamingos(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.OnSpawningFlamingos))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningFlamingosEventArgs), nameof(SpawningFlamingosEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + + // Scp1507Spawner._alpha = ev.Player.ReferenceHub; + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningFlamingosEventArgs), nameof(SpawningFlamingosEventArgs.Player))), + new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.ReferenceHub))), + new(OpCodes.Stsfld, Field(typeof(Scp1507Spawner), nameof(Scp1507Spawner._alpha))), + }); + + index = newInstructions.FindIndex(x => x.Calls(PropertyGetter(typeof(ReferenceHub), nameof(ReferenceHub.AllHubs)))); + + newInstructions.RemoveAt(index); + + // replacing ReferenceHub.AllHubs to SpawningFlamingosEventArgs::SpawnablePlayers + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningFlamingosEventArgs), nameof(SpawningFlamingosEventArgs.SpawnablePlayers))), + new(OpCodes.Call, Method(typeof(SpawningFlamingos), nameof(ReturnEnumerator))), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + + private static HashSet ReturnEnumerator(HashSet enumerable) => enumerable.Select(x => x.ReferenceHub).ToHashSet(); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1507/TapeUsing.cs b/EXILED/Exiled.Events/Patches/Events/Scp1507/TapeUsing.cs new file mode 100644 index 000000000..34118b901 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/TapeUsing.cs @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ +#pragma warning disable CS0618 + + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp1507; + using HarmonyLib; + using InventorySystem.Items.FlamingoTapePlayer; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.UsingTape))] + [HarmonyPatch(typeof(TapeItem), nameof(TapeItem.ServerProcessCmd))] + public class TapeUsing + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ret) + offset; + + Label retLabel = generator.DefineLabel(); + + newInstructions.InsertRange(index, new[] + { + // this; + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + + // true + new(OpCodes.Ldc_I4_1), + + // TapeUsingEventArgs ev = new(Player, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UsingTapeEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Scp1507.OnUsingTape(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.OnUsingTape))), + + // if (!ev.IsAllowed) + // goto retLabel; + new(OpCodes.Callvirt, PropertyGetter(typeof(UsingTapeEventArgs), nameof(UsingTapeEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp2536/FindingPosition.cs b/EXILED/Exiled.Events/Patches/Events/Scp2536/FindingPosition.cs new file mode 100644 index 000000000..8fbaa35ed --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp2536/FindingPosition.cs @@ -0,0 +1,82 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp2536 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Christmas.Scp2536; + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp244; + using Exiled.Events.EventArgs.Scp2536; + using HarmonyLib; + using UnityEngine; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.FindingPosition))] + [HarmonyPatch(typeof(Scp2536Controller), nameof(Scp2536Controller.CanFindPosition))] + internal class FindingPosition + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder ev = generator.DeclareLocal(typeof(FindingPositionEventArgs)); + + int offset = 0; + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldloc_S && x.operand is LocalBuilder { LocalIndex: 5 }) + offset; + + Label continueLabel = (Label)newInstructions[index - 1].operand; + + newInstructions.InsertRange( + index, + new[] + { + // Player.Get(target); + new CodeInstruction(OpCodes.Ldarg_1).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // scp2536Spawnpoint + new(OpCodes.Ldloc_S, 4), + + // true + new(OpCodes.Ldc_I4_1), + + // FindingPositionEventArgs ev = new(Player, Scp2536Spawnpoint, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(FindingPositionEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Scp2536.OnFindingPosition(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.OnFindingPosition))), + + // if (!ev.IsAllowed) continue; + new(OpCodes.Callvirt, PropertyGetter(typeof(FindingPositionEventArgs), nameof(FindingPositionEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, continueLabel), + + // scp2536Spawnpoint = ev.Spawnpoint; + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(FindingPositionEventArgs), nameof(FindingPositionEventArgs.Spawnpoint))), + new(OpCodes.Stloc_S, 4), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp2536/GrantingGift.cs b/EXILED/Exiled.Events/Patches/Events/Scp2536/GrantingGift.cs new file mode 100644 index 000000000..9389addfa --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp2536/GrantingGift.cs @@ -0,0 +1,81 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp2536 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Christmas.Scp2536; + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp2536; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.GrantingGift))] + [HarmonyPatch(typeof(Scp2536GiftController), nameof(Scp2536GiftController.ServerGrantRandomGift))] + internal class GrantingGift + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(GrantingGiftEventArgs)); + + int offset = -1; + int index = newInstructions.FindLastIndex(x => x.LoadsField(Field(typeof(Scp2536GiftBase), nameof(Scp2536GiftBase.ObtainedBy)))) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(hub); + new CodeInstruction(OpCodes.Ldarg_1).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // gift + new(OpCodes.Ldloc_1), + + // true + new(OpCodes.Ldc_I4_1), + + // GrantingGiftEventArgs ev = new(Player, Scp2536GiftBase, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(GrantingGiftEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Scp2536.OnGrantingGift(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.OnGrantingGift))), + + // if (!ev.IsAllowed) + // goto retLabel; + new(OpCodes.Callvirt, PropertyGetter(typeof(GrantingGiftEventArgs), nameof(GrantingGiftEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + + // gift = ev.Gift; + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(GrantingGiftEventArgs), nameof(GrantingGiftEventArgs.Gift))), + new(OpCodes.Stloc_1), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp2536/OpeningGift.cs b/EXILED/Exiled.Events/Patches/Events/Scp2536/OpeningGift.cs new file mode 100644 index 000000000..60c8fe059 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp2536/OpeningGift.cs @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp2536 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Christmas.Scp2536; + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp2536; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.OpeningGift))] + [HarmonyPatch(typeof(Scp2536GiftController), nameof(Scp2536GiftController.ServerInteract))] + internal class OpeningGift + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + + int offset = -3; + int index = newInstructions.FindLastIndex(x => x.Calls(Method(typeof(Scp2536GiftController), nameof(Scp2536GiftController.RpcSetGiftState)))) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(hub); + new CodeInstruction(OpCodes.Ldarg_1).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // OpeningGiftEventArgs ev = new(Player, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(OpeningGiftEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Scp2536.OnOppeningGift(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.OnOppeningGift))), + + // if (!ev.IsAllowed) + // goto retLabel; + new(OpCodes.Callvirt, PropertyGetter(typeof(OpeningGiftEventArgs), nameof(OpeningGiftEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp559/Interacting.cs b/EXILED/Exiled.Events/Patches/Events/Scp559/Interacting.cs new file mode 100644 index 000000000..1c0f3442e --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp559/Interacting.cs @@ -0,0 +1,73 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp559 +{ +#pragma warning disable CS0618 + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp559; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp559), nameof(Handlers.Scp559.Interacting))] + [HarmonyPatch(typeof(Scp559Cake), nameof(Scp559Cake.ServerInteract))] + internal class Interacting + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ret) + offset; + + Label retLabel = generator.DefineLabel(); + + newInstructions.InsertRange( + index, + new[] + { + // Scp559.Get(this); + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Scp559), nameof(Scp559.Get), new[] { typeof(Scp559Cake) })), + + // Player.Get(hub); + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // InteractingScp559EventArgs ev = new(Scp559, Player, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InteractingScp559EventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Scp559.OnInteracting(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp559), nameof(Handlers.Scp559.OnInteracting))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp559EventArgs), nameof(InteractingScp559EventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp559/Spawning.cs b/EXILED/Exiled.Events/Patches/Events/Scp559/Spawning.cs new file mode 100644 index 000000000..8027c34ad --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp559/Spawning.cs @@ -0,0 +1,81 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp559 +{ +#pragma warning disable CS0618 + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp559; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp559), nameof(Handlers.Scp559.Spawning))] + [HarmonyPatch(typeof(Scp559Cake), nameof(Scp559Cake.SetPosition))] + internal class Spawning + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(SpawningEventArgs)); + + newInstructions.InsertRange( + 0, + new CodeInstruction[] + { + // Scp559.Get(this); + new(OpCodes.Ldarg_0), + new(OpCodes.Call, Method(typeof(Scp559), nameof(Scp559.Get), new[] { typeof(Scp559Cake) })), + + // prev + new(OpCodes.Ldarg_1), + + // cur + new(OpCodes.Ldarg_2), + + // true + new(OpCodes.Ldc_I4_1), + + // SpawningEventArgs ev = new(Scp559, Vector3, Vector3, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(SpawningEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Scp559.OnSpawning(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp559), nameof(Handlers.Scp559.OnSpawning))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningEventArgs), nameof(SpawningEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + + // cur = ev.NewPosition; + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningEventArgs), nameof(SpawningEventArgs.NextPosition))), + new(OpCodes.Starg_S, 2), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp939/CreatedAmnesticCloud.cs b/EXILED/Exiled.Events/Patches/Events/Scp939/CreatedAmnesticCloud.cs new file mode 100644 index 000000000..fc11f5954 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp939/CreatedAmnesticCloud.cs @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp939 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp939; + using Exiled.Events.Handlers; + + using HarmonyLib; + + using PlayerRoles.PlayableScps.Scp939; + + using static HarmonyLib.AccessTools; + + /// + /// Patches setter. + /// to add the event. + /// + [EventPatch(typeof(Scp939), nameof(Scp939.CreatedAmnesticCloud))] + [HarmonyPatch(typeof(Scp939AmnesticCloudInstance), nameof(Scp939AmnesticCloudInstance.State), MethodType.Setter)] + internal static class CreatedAmnesticCloud + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder hub = generator.DeclareLocal(typeof(ReferenceHub)); + + Label ret = generator.DefineLabel(); + + newInstructions.InsertRange( + newInstructions.Count - 1, + new[] + { + // if (!ReferenceHub.TryGetHubNetID(_syncOwner, out ReferenceHub owner)) + // return; + new CodeInstruction(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(Scp939AmnesticCloudInstance), nameof(Scp939AmnesticCloudInstance._syncOwner))), + new(OpCodes.Ldloca_S, hub.LocalIndex), + new(OpCodes.Call, Method(typeof(ReferenceHub), nameof(ReferenceHub.TryGetHubNetID))), + new(OpCodes.Brfalse_S, ret), + + // owner + new(OpCodes.Ldloc_S, hub.LocalIndex), + + // this + new(OpCodes.Ldarg_0), + + // Scp939.OnCreatedAmnesticCloud(new CreatedAmnesticCloudEventArgs(owner, this)); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(CreatedAmnesticCloudEventArgs))[0]), + new(OpCodes.Call, Method(typeof(Scp939), nameof(Scp939.OnCreatedAmnesticCloud))), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(ret); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp939/ValidatingVisibility.cs b/EXILED/Exiled.Events/Patches/Events/Scp939/ValidatingVisibility.cs index b8d3462f0..d12e690c6 100644 --- a/EXILED/Exiled.Events/Patches/Events/Scp939/ValidatingVisibility.cs +++ b/EXILED/Exiled.Events/Patches/Events/Scp939/ValidatingVisibility.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. @@ -7,26 +7,147 @@ namespace Exiled.Events.Patches.Events.Scp939 { - #pragma warning disable SA1313 + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection.Emit; + + using Exiled.API.Enums; + using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp939; using HarmonyLib; + using InventorySystem.Items; + using Mirror; + using PlayerRoles.PlayableScps.Scp939; + using static HarmonyLib.AccessTools; + /// /// Patches - /// to add the event. + /// to add the event. /// [EventPatch(typeof(Handlers.Scp939), nameof(Handlers.Scp939.ValidatingVisibility))] [HarmonyPatch(typeof(Scp939VisibilityController), nameof(Scp939VisibilityController.ValidateVisibility))] internal class ValidatingVisibility { - private static void Postfix(Scp939VisibilityController __instance, ReferenceHub hub, ref bool __result) + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder ev = generator.DeclareLocal(typeof(ValidatingVisibilityEventArgs)); + + Label ret = generator.DefineLabel(); + Label end = generator.DefineLabel(); + + int offset = 0; + int index = newInstructions.FindIndex(i => i.LoadsConstant(0)) + offset; + + newInstructions[index].labels.Add(ret); + + newInstructions.InsertRange(index, StaticCallEvent(generator, ev, ret, newInstructions[index], Scp939VisibilityState.None, false)); + + offset = 0; + index = newInstructions.FindIndex(i => i.LoadsConstant(1)) + offset; + + newInstructions.InsertRange(index, StaticCallEvent(generator, ev, ret, newInstructions[index], Scp939VisibilityState.SeenAsScp)); + + offset = 2; + index = newInstructions.FindIndex(i => i.Calls(PropertyGetter(typeof(AlphaWarheadController), nameof(AlphaWarheadController.Detonated)))) + offset; + + newInstructions.InsertRange(index, StaticCallEvent(generator, ev, ret, newInstructions[index], Scp939VisibilityState.SeenByDetonation)); + + offset = 0; + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldloc_3) + offset; + + // just pre-check for SeenByLastTime or NotSeen VisibilityState, and then il inject + newInstructions.InsertRange(index, Enumerable.Concat( + new CodeInstruction[] + { + new CodeInstruction(OpCodes.Ldc_I4, (int)Scp939VisibilityState.NotSeen).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Ldloc_3), + new(OpCodes.Brfalse_S, end), + new(OpCodes.Pop), + new(OpCodes.Ldc_I4, (int)Scp939VisibilityState.SeenByLastTime), + new CodeInstruction(OpCodes.Nop).WithLabels(end), + }, + CallEvent(generator, ev, ret))); + + offset = 0; + index = newInstructions.FindLastIndex(i => i.LoadsField(Field(typeof(Scp939VisibilityController), nameof(Scp939VisibilityController.LastSeen)))) + offset; + + newInstructions.InsertRange(index, StaticCallEvent(generator, ev, ret, newInstructions[index], Scp939VisibilityState.SeenByRange)); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + + // helper method for injecting instructions + private static IEnumerable StaticCallEvent(ILGenerator generator, LocalBuilder ev, Label ret, CodeInstruction insertInstuction, Scp939VisibilityState state, bool setLabel = true) + { + CodeInstruction first = new CodeInstruction(OpCodes.Ldc_I4, (int)state); + + if (setLabel) + { + first.labels.AddRange(insertInstuction.ExtractLabels()); + } + + yield return first; + + foreach (CodeInstruction z in CallEvent(generator, ev, ret)) + { + yield return z; + } + } + + // mail il logic + private static IEnumerable CallEvent(ILGenerator generator, LocalBuilder ev, Label ret) + { + Label cnt = generator.DefineLabel(); + + // ...VisibilityState loaded in stack + // ValidatingVisibilityEventArgs ev = new(state, scp939, target) + yield return new(OpCodes.Ldarg_0); + yield return new(OpCodes.Callvirt, PropertyGetter(typeof(Scp939VisibilityController), nameof(Scp939VisibilityController.Owner))); + yield return new(OpCodes.Ldarg_1); + yield return new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ValidatingVisibilityEventArgs))[0]); + yield return new(OpCodes.Dup); + yield return new(OpCodes.Dup); + yield return new(OpCodes.Stloc_S, ev.LocalIndex); + + // Scp939.OnValidatingVisibility(ev) + // if (!ev.IsAllowed) + // return false; + yield return new(OpCodes.Call, Method(typeof(Handlers.Scp939), nameof(Handlers.Scp939.OnValidatingVisibility))); + yield return new(OpCodes.Callvirt, PropertyGetter(typeof(ValidatingVisibilityEventArgs), nameof(ValidatingVisibilityEventArgs.IsAllowed))); + yield return new(OpCodes.Brfalse_S, ret); + + // if (IsLateSeen) + // ValidatingVisibility.SetToLastSeen(target); + // return true; + yield return new(OpCodes.Ldloc_S, ev.LocalIndex); + yield return new(OpCodes.Callvirt, PropertyGetter(typeof(ValidatingVisibilityEventArgs), nameof(ValidatingVisibilityEventArgs.IsLateSeen))); + yield return new(OpCodes.Brfalse_S, cnt); + + yield return new(OpCodes.Ldarg_1); + yield return new(OpCodes.Call, Method(typeof(ValidatingVisibility), nameof(ValidatingVisibility.SetToLastSeen))); + + yield return new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(cnt); + yield return new(OpCodes.Ret); + } + + private static void SetToLastSeen(ReferenceHub target) { - ValidatingVisibilityEventArgs ev = new(__instance.Owner, hub, __result); - Handlers.Scp939.OnValidatingVisibility(ev); - __result = ev.IsAllowed; + Scp939VisibilityController.LastSeen[target.netId] = new Scp939VisibilityController.LastSeenInfo() + { + Time = NetworkTime.time, + }; } } -} \ No newline at end of file +} diff --git a/EXILED/Exiled.Events/Patches/Events/Server/CompletingObjective.cs b/EXILED/Exiled.Events/Patches/Events/Server/CompletingObjective.cs new file mode 100644 index 000000000..cf9f9aacb --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Server/CompletingObjective.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Server +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Server; + using HarmonyLib; + using Respawning.Objectives; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.CompletingObjective))] + [HarmonyPatch(typeof(FactionObjectiveBase), nameof(FactionObjectiveBase.ServerSendUpdate))] + internal class CompletingObjective + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label returnLabel = generator.DefineLabel(); + + newInstructions.InsertRange(0, new CodeInstruction[] + { + // this + new(OpCodes.Ldarg_0), + + // true + new(OpCodes.Ldc_I4_1), + + // CompletingObjectiveEventArgs ev = new(this, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(CompletingObjectiveEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Server.OnCompletingObjective(ev); + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnCompletingObjective))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(CompletingObjectiveEventArgs), nameof(CompletingObjectiveEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, returnLabel), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(returnLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs b/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs index 0700f5419..7792e7cdd 100644 --- a/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs +++ b/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs @@ -18,15 +18,19 @@ namespace Exiled.Events.Patches.Events.Server using static HarmonyLib.AccessTools; /// - /// 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); } diff --git a/EXILED/Exiled.Events/Patches/Events/Warhead/DeadmanSwitchStart.cs b/EXILED/Exiled.Events/Patches/Events/Warhead/DeadmanSwitchStart.cs new file mode 100644 index 000000000..9d88dd060 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Warhead/DeadmanSwitchStart.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Warhead +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Warhead; + using Handlers; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add and events. + /// + [EventPatch(typeof(Warhead), nameof(Warhead.DeadmanSwitchInitiating))] + [HarmonyPatch(typeof(DeadmanSwitch), nameof(DeadmanSwitch.InitiateProtocol))] + internal static class DeadmanSwitchStart + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label retLabel = generator.DefineLabel(); + LocalBuilder ev = generator.DeclareLocal(typeof(DetonatingEventArgs)); + + newInstructions.InsertRange(0, new CodeInstruction[] + { + // if (!Exiled.API.Features.Warhead.DeadmanSwitchEnabled) + // return; + new(OpCodes.Call, PropertyGetter(typeof(Exiled.API.Features.Warhead), nameof(Exiled.API.Features.Warhead.DeadmanSwitchEnabled))), + new(OpCodes.Brfalse_S, retLabel), + + // DeadmanSwitchInitiatingEventArgs ev = new(); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DeadmanSwitchInitiatingEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Warhead.OnDeadmanSwitchInitiating(ev); + new(OpCodes.Call, Method(typeof(Warhead), nameof(Warhead.OnDeadmanSwitchInitiating))), + + // if (ev.IsAllowed) + // goto retLabel; + new(OpCodes.Callvirt, PropertyGetter(typeof(DeadmanSwitchInitiatingEventArgs), nameof(DeadmanSwitchInitiatingEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Fixes/FixElevatorChamberAwake.cs b/EXILED/Exiled.Events/Patches/Fixes/FixElevatorChamberAwake.cs new file mode 100644 index 000000000..073931c8f --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Fixes/FixElevatorChamberAwake.cs @@ -0,0 +1,28 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Fixes +{ + using Exiled.Events.Attributes; + using HarmonyLib; + using Interactables.Interobjects; + + /// + /// Patches 's setter. + /// Fix for the event having its Lift value being null. + /// + [HarmonyPatch(typeof(ElevatorChamber), nameof(ElevatorChamber.Awake))] + internal class FixElevatorChamberAwake + { +#pragma warning disable SA1313 + private static void Postfix(ElevatorChamber __instance) +#pragma warning restore SA1313 + { + __instance._floorDoors = ElevatorDoor.GetDoorsForGroup(__instance.AssignedGroup); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Generic/CoffeeListAdd.cs b/EXILED/Exiled.Events/Patches/Generic/CoffeeListAdd.cs new file mode 100644 index 000000000..8901e28bb --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/CoffeeListAdd.cs @@ -0,0 +1,25 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using Exiled.API.Features; + using HarmonyLib; +#pragma warning disable SA1313 + + /// + /// Patches to control coffee list. + /// + [HarmonyPatch(typeof(global::Coffee), nameof(global::Coffee.Start))] + internal class CoffeeListAdd + { + private static void Postfix(global::Coffee __instance) + { + _ = new Coffee(__instance); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs b/EXILED/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs index 17b57d822..bafb4a284 100644 --- a/EXILED/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs +++ b/EXILED/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs @@ -99,7 +99,7 @@ public static bool CheckFriendlyFirePlayerRules(Footprint attackerFootprint, Ref // Only check friendlyFire if the FootPrint hasn't changed (Fix for Grenade not dealing damage because it's from a dead player) if (!attackerFootprint.CompareLife(new Footprint(attackerFootprint.Hub))) - return true; + return HitboxIdentity.IsDamageable(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); // Check if attackerFootprint.Hub or victimHub is null and log debug information if (attackerFootprint.Hub is null || victimHub is null) diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp559List.cs b/EXILED/Exiled.Events/Patches/Generic/Scp559List.cs new file mode 100644 index 000000000..1bfb1a4f1 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/Scp559List.cs @@ -0,0 +1,28 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ +#pragma warning disable SA1313 +#pragma warning disable CS0618 + + using Exiled.API.Features; + using HarmonyLib; + + /// + /// Patches + /// to control . + /// + [HarmonyPatch(typeof(Scp559Cake), nameof(Scp559Cake.Start))] + internal class Scp559List + { + private static void Postfix(Scp559Cake __instance) + { + _ = new Scp559(__instance); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp956Capybara.cs b/EXILED/Exiled.Events/Patches/Generic/Scp956Capybara.cs new file mode 100644 index 000000000..54d638086 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/Scp956Capybara.cs @@ -0,0 +1,28 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using Exiled.API.Features; + using HarmonyLib; + +#pragma warning disable SA1313 + + /// + /// Patches + /// to implement better pinata capybara. + /// + [HarmonyPatch(typeof(Scp956Pinata), nameof(Scp956Pinata.UpdateAi))] + internal class Scp956Capybara + { + private static void Postfix(Scp956Pinata __instance) + { + if (Scp956.IsCapybara) + __instance.Network_carpincho = 69; + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Loader/AutoUpdateFiles.cs b/EXILED/Exiled.Loader/AutoUpdateFiles.cs index 9d7d1dfb7..c04a1d00e 100644 --- a/EXILED/Exiled.Loader/AutoUpdateFiles.cs +++ b/EXILED/Exiled.Loader/AutoUpdateFiles.cs @@ -17,6 +17,6 @@ public static class AutoUpdateFiles /// /// Gets which SCP: SL version generated Exiled. /// - public static readonly Version RequiredSCPSLVersion = new(14, 0, 0, 1); + public static readonly Version RequiredSCPSLVersion = new(14, 0, 0, 2); } } \ No newline at end of file diff --git a/EXILED/docs/articles/SCPSLRessources/NW_Documentation.md b/EXILED/docs/articles/SCPSLRessources/NW_Documentation.md index fcde0eadb..66dd2d881 100644 --- a/EXILED/docs/articles/SCPSLRessources/NW_Documentation.md +++ b/EXILED/docs/articles/SCPSLRessources/NW_Documentation.md @@ -17,7 +17,7 @@ title: NW Documentation --- -Last Update (14.0.0.0) +Last Update (14.0.0.2) ### Index @@ -46,6 +46,7 @@ Last Update (14.0.0.0) - [Authorization](#authorization) - [AutoHideType](#autohidetype) - [AutosyncInstantiationStatus](#autosyncinstantiationstatus) +- [AutosyncMessageType](#autosyncmessagetype) - [BadgePreferences](#badgepreferences) - [BadgeVisibilityPreferences](#badgevisibilitypreferences) - [BanType](#bantype) @@ -150,7 +151,7 @@ Last Update (14.0.0.0) - [HintTranslations](#hinttranslations) - [HintType](#hinttype) - [HitboxType](#hitboxtype) -- [Holidays](#holidays) +- [HolidayType](#holidaytype) - [HotkeysTranslation](#hotkeystranslation) - [HttpQueryMode](#httpquerymode) - [IcomText](#icomtext) @@ -181,8 +182,8 @@ Last Update (14.0.0.0) - [MapGenerationPhase](#mapgenerationphase) - [MappingLifetime](#mappinglifetime) - [MessageHeader](#messageheader) -- [MessageHeader](#messageheader) - [MessageImportance](#messageimportance) +- [MessageInterceptionResult](#messageinterceptionresult) - [MessageType](#messagetype) - [MessageType](#messagetype) - [MicroHidFiringMode](#microhidfiringmode) @@ -230,6 +231,7 @@ Last Update (14.0.0.0) - [RadioCommand](#radiocommand) - [RadioRangeLevel](#radiorangelevel) - [RejectionReason](#rejectionreason) +- [ReloaderMessageHeader](#reloadermessageheader) - [RemoteAdminResponseFlags](#remoteadminresponseflags) - [RemovalMode](#removalmode) - [ReproProjectAssetType](#reproprojectassettype) @@ -243,7 +245,6 @@ Last Update (14.0.0.0) - [RoomShape](#roomshape) - [RootCullablePriority](#rootcullablepriority) - [RoundRestartType](#roundrestarttype) -- [RpcHeader](#rpcheader) - [RpcStateMsg](#rpcstatemsg) - [RpcType](#rpctype) - [RpcType](#rpctype) @@ -255,6 +256,7 @@ Last Update (14.0.0.0) - [RpcType](#rpctype) - [RpcType](#rpctype) - [RpcType](#rpctype) +- [RpcType](#rpctype) - [ScanSequenceStep](#scansequencestep) - [Scp0492SoundId](#scp0492soundid) - [Scp079HudTranslation](#scp079hudtranslation) @@ -784,6 +786,18 @@ Last Update (14.0.0.0) +### AutosyncMessageType + +
InventorySystem.Items.Autosync.AutosyncMessageType + +``` + [0] = RpcInstance + [1] = RpcTemplate + [2] = Cmd +``` + +
+ ### BadgePreferences
ServerRoles+BadgePreferences @@ -1601,10 +1615,10 @@ Last Update (14.0.0.0)
CustomPlayerEffects.StatusEffectBase+EffectClassification ``` - [0] = Negative - [1] = Mixed - [2] = Positive - [3] = Technical + [0] = Technical + [1] = Negative + [2] = Mixed + [3] = Positive ```
@@ -2004,6 +2018,10 @@ Last Update (14.0.0.0) [5] = Nuke [6] = Scp244 [7] = BecomingFlamingo + [8] = HalloweenInside + [9] = HalloweenOutside + [10] = ChristmasInside + [11] = ChristmasOutside ```
@@ -2292,20 +2310,15 @@ Last Update (14.0.0.0) -### Holidays +### HolidayType -
Holidays +
MapGeneration.Holidays.HolidayType ``` - [0] = NoHoliday - [1] = Halloween - [2] = Christmas + [0] = None + [1] = Christmas + [2] = Halloween [3] = AprilFools - [4] = October - [5] = December - [6] = FirstHalfOfApril - [7] = OctoberOrDecember - [-1] = Always ```
@@ -2863,20 +2876,6 @@ Last Update (14.0.0.0) ### MessageHeader -
InventorySystem.Items.Firearms.Modules.AnimatorReloaderModuleBase+MessageHeader - -``` - [0] = Custom - [1] = Reload - [2] = Unload - [3] = Stop - [4] = RequestRejected -``` - -
- -### MessageHeader -
InventorySystem.Items.Firearms.Modules.AutomaticActionModule+MessageHeader ``` @@ -2903,6 +2902,18 @@ Last Update (14.0.0.0)
+### MessageInterceptionResult + +
InventorySystem.Items.Autosync.MessageInterceptionResult + +``` + [0] = Continue + [1] = ResetAndContinue + [2] = Stop +``` + +
+ ### MessageType
InventorySystem.Items.Firearms.Modules.DisruptorActionModule+MessageType @@ -3646,6 +3657,20 @@ Last Update (14.0.0.0)
+### ReloaderMessageHeader + +
InventorySystem.Items.Firearms.Modules.AnimatorReloaderModuleBase+ReloaderMessageHeader + +``` + [0] = Reload + [1] = Unload + [2] = Stop + [3] = RequestRejected + [4] = Custom +``` + +
+ ### RemoteAdminResponseFlags
RemoteAdminResponse+RemoteAdminResponseFlags @@ -3877,17 +3902,6 @@ Last Update (14.0.0.0)
-### RpcHeader - -
InventorySystem.Items.Firearms.Modules.AnimationToggleableReloaderModule+RpcHeader - -``` - [0] = SyncLoadable - [1] = Cancel -``` - -
- ### RpcStateMsg
PlayerRoles.PlayableScps.Scp939.Mimicry.MimicPointController+RpcStateMsg @@ -3976,6 +3990,17 @@ Last Update (14.0.0.0) ### RpcType +
InventorySystem.Items.Firearms.Modules.AnimationToggleableReloaderModule+RpcType + +``` + [0] = LoadableAmmoSync + [1] = StopAnimations +``` + +
+ +### RpcType +
InventorySystem.Items.Firearms.Modules.EventBasedEquipperModule+RpcType ``` @@ -5175,7 +5200,7 @@ Last Update (14.0.0.0)
Damage Handlers -```md title="Latest Updated: 14.0.0.0" +```md title="Latest Updated: 14.0.0.2" All available DamageHandlers + Symbol ':' literally means "inherits from" diff --git a/EXILED/docs/docs.csproj b/EXILED/docs/docs.csproj index f7fdc32d9..d8e9026b9 100644 --- a/EXILED/docs/docs.csproj +++ b/EXILED/docs/docs.csproj @@ -18,7 +18,6 @@ -