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