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/EffectType.cs b/EXILED/Exiled.API/Enums/EffectType.cs index b2894486e..5055dfd4c 100644 --- a/EXILED/Exiled.API/Enums/EffectType.cs +++ b/EXILED/Exiled.API/Enums/EffectType.cs @@ -259,5 +259,25 @@ public enum EffectType /// . /// Blurred, + + /// + /// Makes you a flamingo. + /// + BecomingFlamingo, + + /// + /// Makes you a Child after eating Cake. + /// + Scp559, + + /// + /// Scp956 found you. + /// + Scp956Target, + + /// + /// you are snowed. + /// + Snowed, } } 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/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..dc9e09857 100644 --- a/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs +++ b/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs @@ -78,6 +78,10 @@ public static class EffectTypeExtension { EffectType.SeveredEyes, typeof(SeveredEyes) }, { EffectType.PitDeath, typeof(PitDeath) }, { EffectType.Blurred, typeof(Blurred) }, + { EffectType.BecomingFlamingo, typeof(BecomingFlamingo) }, + { EffectType.Scp559, typeof(Scp559Effect) }, + { EffectType.Scp956Target, typeof(Scp956Target) }, + { EffectType.Snowed, typeof(Snowed) }, }); /// 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/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/Map.cs b/EXILED/Exiled.API/Features/Map.cs index 081999068..ce4d03345 100644 --- a/EXILED/Exiled.API/Features/Map.cs +++ b/EXILED/Exiled.API/Features/Map.cs @@ -367,6 +367,10 @@ internal static void ClearCache() Firearm.ItemTypeToFirearmInstance.Clear(); Firearm.BaseCodesValue.Clear(); Firearm.AvailableAttachmentsValue.Clear(); + + Scp559.CakeToWrapper.Clear(); + + Coffee.BaseToWrapper.Clear(); } } } 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..e55395638 --- /dev/null +++ b/EXILED/Exiled.API/Features/Scp559.cs @@ -0,0 +1,136 @@ +// ----------------------------------------------------------------------- +// +// 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. + /// + 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.Events/EventArgs/Interfaces/IScp1507Event.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs new file mode 100644 index 000000000..accb59a56 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Interfaces +{ + using Exiled.API.Features.Roles; + + /// + /// Event args used for all related events. + /// + 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..b1efbf902 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp559Event.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.Interfaces +{ + /// + /// Defines the base contract for all related events. + /// + 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/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/Scp1507/AttackingDoorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs new file mode 100644 index 000000000..a288e959c --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs @@ -0,0 +1,47 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + 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. + /// + 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..529588058 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + using Exiled.API.Features; + using Exiled.API.Features.Roles; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before SCP-1507 screams. + /// + 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..60ce27ff6 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1507/SpawningFlamingosEventArgs.cs @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp1507 +{ + 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. + /// + 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/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..a4d65f795 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp559/InteractingScp559EventArgs.cs @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp559 +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before a player interacts with SCP-559. + /// + 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..1379dcc37 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp559/SpawningEventArgs.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.Scp559 +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using UnityEngine; + + /// + /// Contains all information before SCP-559 spawns. + /// + 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/Handlers/Internal/MapGenerated.cs b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs index 1320ad76a..60ec67979 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(1, Handlers.Map.OnGenerated); } diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index d87675450..218d84113 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -563,6 +563,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. /// @@ -1210,6 +1215,12 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// The instance. public static void OnChangingDisruptorMode(ChangingDisruptorModeEventArgs ev) => ChangingDisruptorMode.InvokeSafely(ev); + /// + /// Called before player interacts with coffee cup. + /// + /// The instance. + public static void OnDrinkingCoffee(DrinkingCoffeeEventArgs ev) => DrinkingCoffee.InvokeSafely(ev); + /// /// Called before pre-authenticating a . /// diff --git a/EXILED/Exiled.Events/Handlers/Scp1507.cs b/EXILED/Exiled.Events/Handlers/Scp1507.cs new file mode 100644 index 000000000..c2f15eb05 --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Scp1507.cs @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers +{ + using Exiled.Events.EventArgs.Scp1507; + using Exiled.Events.Features; + +#pragma warning disable SA1623 + + /// + /// SCP-1507 related events. + /// + 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(); + + /// + /// 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); + } +} \ 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..dc6ddd029 --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Scp559.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers +{ + using Exiled.Events.EventArgs.Scp559; + using Exiled.Events.Features; +#pragma warning disable SA1623 + + /// + /// All SCP-559 related events. + /// + 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/Patches/Events/Player/DrinkingCoffee.cs b/EXILED/Exiled.Events/Patches/Events/Player/DrinkingCoffee.cs new file mode 100644 index 000000000..a22175690 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/DrinkingCoffee.cs @@ -0,0 +1,74 @@ +// ----------------------------------------------------------------------- +// +// 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/Scp1507/AttackingDoor.cs b/EXILED/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs new file mode 100644 index 000000000..bbe2df585 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Doors; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp1507; + using HarmonyLib; + using Interactables.Interobjects.DoorUtils; + 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 = -5; + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ldloc_S) + offset; + + Label continueLabel = generator.DefineLabel(); + + newInstructions[index].labels.Add(continueLabel); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // Player.Get(this.Owner); + new(OpCodes.Ldarg_0), + 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), + }); + + 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..e9efe18fb --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/Scream.cs @@ -0,0 +1,67 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ + 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..cc1d524b8 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1507/SpawningFlamingos.cs @@ -0,0 +1,98 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1507 +{ + 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/Scp2536/FindingPosition.cs b/EXILED/Exiled.Events/Patches/Events/Scp2536/FindingPosition.cs new file mode 100644 index 000000000..9743c9388 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp2536/FindingPosition.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.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) })), + + // foundSpot + new(OpCodes.Ldarg_2), + new(OpCodes.Ldind_Ref), + + // 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), + + // foundSpot = ev.Spawnpoint; + new(OpCodes.Ldarg_2), + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(FindingPositionEventArgs), nameof(FindingPositionEventArgs.Spawnpoint))), + new(OpCodes.Stind_Ref), + + new(OpCodes.Br_S, 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/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..9c18800f6 --- /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))] + [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.OnGrantingGift(ev); + new(OpCodes.Call, Method(typeof(Handlers.Scp2536), nameof(Handlers.Scp2536.OnGrantingGift))), + + // 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..886723762 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp559/Interacting.cs @@ -0,0 +1,72 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp559 +{ + 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..5e703f50b --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp559/Spawning.cs @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp559 +{ + 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/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/Scp559List.cs b/EXILED/Exiled.Events/Patches/Generic/Scp559List.cs new file mode 100644 index 000000000..7669cc930 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/Scp559List.cs @@ -0,0 +1,27 @@ +// ----------------------------------------------------------------------- +// +// 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 . + /// + [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