From 06c942072be5eeea28366c0f3c43c96f7a31fd7c Mon Sep 17 00:00:00 2001 From: VALERA771 Date: Thu, 21 Dec 2023 12:47:47 +0300 Subject: [PATCH 01/10] scp-1507 --- Exiled.API/Features/Roles/Scp1507Role.cs | 46 ++++++++++++ .../EventArgs/Interfaces/IScp1507Event.cs | 22 ++++++ .../Scp1507/AttackingDoorEventArgs.cs | 46 ++++++++++++ Exiled.Events/Handlers/Scp1507.cs | 31 ++++++++ .../Patches/Events/Scp1507/AttackingDoor.cs | 72 +++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 Exiled.API/Features/Roles/Scp1507Role.cs create mode 100644 Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs create mode 100644 Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs create mode 100644 Exiled.Events/Handlers/Scp1507.cs create mode 100644 Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs new file mode 100644 index 0000000000..528a89a3df --- /dev/null +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Roles +{ + using Exiled.API.Enums; + using Exiled.API.Interfaces; + using PlayerRoles; + + using BaseRole = PlayerRoles.PlayableScps.Scp1507.Scp1507Role; + + /// + /// A wrapper for . + /// + public class Scp1507Role : Role, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// the base . + public Scp1507Role(BaseRole baseRole) + : base(baseRole) + { + Base = baseRole; + } + + /// + public override RoleTypeId Type => RoleTypeId.Flamingo; + + /// + public BaseRole Base { get; } + + /// + /// Gets or sets sync spawn reason for role. + /// + public SpawnReason SyncSpawnReason + { + get => (SpawnReason)Base._syncSpawnReason; + set => Base._syncSpawnReason = (RoleChangeReason)value; + } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs b/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs new file mode 100644 index 0000000000..837ebae8e4 --- /dev/null +++ b/Exiled.Events/EventArgs/Interfaces/IScp1507Event.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled 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.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs b/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs new file mode 100644 index 0000000000..6918e4808f --- /dev/null +++ b/Exiled.Events/EventArgs/Scp1507/AttackingDoorEventArgs.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled 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; + + /// + /// 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, Door door, bool isAllowed = true) + { + Player = player; + Scp1507 = player.Role.As(); + Door = door; + 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.Events/Handlers/Scp1507.cs b/Exiled.Events/Handlers/Scp1507.cs new file mode 100644 index 0000000000..1d79575eca --- /dev/null +++ b/Exiled.Events/Handlers/Scp1507.cs @@ -0,0 +1,31 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled 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(); + + /// + /// Called before SCP-1507 attacks door. + /// + /// The instance. + public static void OnAttackingDoor(AttackingDoorEventArgs ev) => AttackingDoor.InvokeSafely(ev); + } +} \ No newline at end of file diff --git a/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs b/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs new file mode 100644 index 0000000000..e14ab7d245 --- /dev/null +++ b/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs @@ -0,0 +1,72 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled 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.EventArgs.Scp1507; + using HarmonyLib; + using Interactables.Interobjects.DoorUtils; + using PlayerRoles.PlayableScps.Scp1507; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [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 = 2; + 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[] + { + 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) })), + + new(OpCodes.Ldarg_3), + new(OpCodes.Call, Method(typeof(Door), nameof(Door.Get), new[] { typeof(DoorVariant) })), + + new(OpCodes.Ldc_I4_1), + + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AttackingDoorEventArgs))[0]), + new(OpCodes.Dup), + + new(OpCodes.Call, Method(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.OnAttackingDoor))), + + new(OpCodes.Callvirt, PropertyGetter(typeof(AttackingDoorEventArgs), nameof(AttackingDoorEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + + 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 From 6dd3e5ab046b299c96d4c3e7f25afbbbf9dcfd34 Mon Sep 17 00:00:00 2001 From: VALERA771 Date: Thu, 21 Dec 2023 12:55:25 +0300 Subject: [PATCH 02/10] minor changes --- Exiled.API/Features/Roles/Scp1507Role.cs | 2 +- Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index 528a89a3df..79101bd6e6 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -32,7 +32,7 @@ public Scp1507Role(BaseRole baseRole) public override RoleTypeId Type => RoleTypeId.Flamingo; /// - public BaseRole Base { get; } + public new BaseRole Base { get; } /// /// Gets or sets sync spawn reason for role. diff --git a/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs b/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs index e14ab7d245..00c47c9703 100644 --- a/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs +++ b/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs @@ -42,23 +42,30 @@ private static IEnumerable Transpiler(IEnumerable Date: Thu, 21 Dec 2023 12:57:43 +0300 Subject: [PATCH 03/10] added role --- Exiled.API/Features/Roles/Role.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Exiled.API/Features/Roles/Role.cs b/Exiled.API/Features/Roles/Role.cs index 96f7a3b687..63d0a53986 100644 --- a/Exiled.API/Features/Roles/Role.cs +++ b/Exiled.API/Features/Roles/Role.cs @@ -10,15 +10,12 @@ namespace Exiled.API.Features.Roles using System; using Enums; - using Exiled.API.Features.Core; using Exiled.API.Features.Spawn; using Exiled.API.Interfaces; using Extensions; - using PlayerRoles; using PlayerRoles.PlayableScps.Scp049.Zombies; - using UnityEngine; using FilmmakerGameRole = PlayerRoles.Filmmaker.FilmmakerRole; @@ -28,6 +25,7 @@ namespace Exiled.API.Features.Roles using Scp079GameRole = PlayerRoles.PlayableScps.Scp079.Scp079Role; using Scp096GameRole = PlayerRoles.PlayableScps.Scp096.Scp096Role; using Scp106GameRole = PlayerRoles.PlayableScps.Scp106.Scp106Role; + using Scp1507GameRole = PlayerRoles.PlayableScps.Scp1507.Scp1507Role; using Scp173GameRole = PlayerRoles.PlayableScps.Scp173.Scp173Role; using Scp3114GameRole = PlayerRoles.PlayableScps.Scp3114.Scp3114Role; using Scp939GameRole = PlayerRoles.PlayableScps.Scp939.Scp939Role; @@ -229,6 +227,7 @@ public virtual void Set(RoleTypeId newRole, SpawnReason reason, RoleSpawnFlags s SpectatorGameRole spectatorRole => new SpectatorRole(spectatorRole), HumanGameRole humanRole => new HumanRole(humanRole), FilmmakerGameRole filmmakerRole => new FilmMakerRole(filmmakerRole), + Scp1507GameRole scp1507Role => new Scp1507Role(scp1507Role), _ => new NoneRole(role), }; } From 25850800dfa79f55d0eb9b2c98833ed000ff2c1e Mon Sep 17 00:00:00 2001 From: VALERA771 Date: Thu, 21 Dec 2023 21:59:06 +0300 Subject: [PATCH 04/10] new --- Exiled.API/Features/Roles/Scp1507Role.cs | 54 ++++++++++++++++--- .../Patches/Events/Scp1507/AttackingDoor.cs | 2 + 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index 79101bd6e6..39fccec8b0 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -10,13 +10,15 @@ namespace Exiled.API.Features.Roles using Exiled.API.Enums; using Exiled.API.Interfaces; using PlayerRoles; + using PlayerRoles.PlayableScps.Scp1507; + using PlayerRoles.Subroutines; using BaseRole = PlayerRoles.PlayableScps.Scp1507.Scp1507Role; /// /// A wrapper for . /// - public class Scp1507Role : Role, IWrapper + public class Scp1507Role : Role, IWrapper, ISubroutinedScpRole { /// /// Initializes a new instance of the class. @@ -26,21 +28,61 @@ public Scp1507Role(BaseRole baseRole) : base(baseRole) { Base = baseRole; + + SubroutineModule = baseRole.SubroutineModule; + + if (!SubroutineModule.TryGetSubroutine(out Scp1507AttackAbility attackAbility)) + Log.Error($"Attack ability is not a subroutine for {nameof(Scp1507Role)}"); + + AttackAbility = attackAbility; + + if (!SubroutineModule.TryGetSubroutine(out Scp1507SwarmAbility swarmAbility)) + Log.Error($"Swarm ability is not a subroutine for {nameof(Scp1507Role)}"); + + SwarmAbility = swarmAbility; + + if (!SubroutineModule.TryGetSubroutine(out Scp1507VocalizeAbility vocalizeAbility)) + Log.Error($"Vocalize ability is not a subroutine for {nameof(Scp1507Role)}"); + + VocalizeAbility = vocalizeAbility; } /// - public override RoleTypeId Type => RoleTypeId.Flamingo; + public override RoleTypeId Type => Base._roleTypeId; /// public new BaseRole Base { get; } + /// + public SubroutineManagerModule SubroutineModule { get; } + /// - /// Gets or sets sync spawn reason for role. + /// Gets the for this role. /// - public SpawnReason SyncSpawnReason + public Scp1507AttackAbility AttackAbility { get; } + + /// + /// Gets the for this role. + /// + public Scp1507SwarmAbility SwarmAbility { get; } + + /// + /// Gets the for this role. + /// + public Scp1507VocalizeAbility VocalizeAbility { get; } + + /// + /// Gets or sets how much damage should deal SCP-1507. + /// + public float Damage { - get => (SpawnReason)Base._syncSpawnReason; - set => Base._syncSpawnReason = (RoleChangeReason)value; + get => AttackAbility._damage; + set => AttackAbility._damage = value; } + + /// + /// Gets the delay between attacks. + /// + public float AttackDelay => AttackAbility.AttackDelay; } } \ No newline at end of file diff --git a/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs b/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs index 00c47c9703..30f60e6342 100644 --- a/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs +++ b/Exiled.Events/Patches/Events/Scp1507/AttackingDoor.cs @@ -13,6 +13,7 @@ namespace Exiled.Events.Patches.Events.Scp1507 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; @@ -24,6 +25,7 @@ namespace Exiled.Events.Patches.Events.Scp1507 /// Patches /// to add event. /// + [EventPatch(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.AttackingDoor))] [HarmonyPatch(typeof(Scp1507AttackAbility), nameof(Scp1507AttackAbility.TryAttackDoor))] internal class AttackingDoor { From 8cd9e5bdf92da973f39a854bacc510b5fd9f5f2f Mon Sep 17 00:00:00 2001 From: Yamato Date: Thu, 21 Dec 2023 23:43:15 +0100 Subject: [PATCH 05/10] IHumeShieldRole --- Exiled.API/Features/Roles/Scp1507Role.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index 39fccec8b0..5f17198013 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -10,6 +10,7 @@ namespace Exiled.API.Features.Roles using Exiled.API.Enums; using Exiled.API.Interfaces; using PlayerRoles; + using PlayerRoles.PlayableScps.HumeShield; using PlayerRoles.PlayableScps.Scp1507; using PlayerRoles.Subroutines; @@ -18,7 +19,7 @@ namespace Exiled.API.Features.Roles /// /// A wrapper for . /// - public class Scp1507Role : Role, IWrapper, ISubroutinedScpRole + public class Scp1507Role : Role, IWrapper, ISubroutinedScpRole, IHumeShieldRole { /// /// Initializes a new instance of the class. @@ -30,6 +31,7 @@ public Scp1507Role(BaseRole baseRole) Base = baseRole; SubroutineModule = baseRole.SubroutineModule; + HumeShieldModule = baseRole.HumeShieldModule; if (!SubroutineModule.TryGetSubroutine(out Scp1507AttackAbility attackAbility)) Log.Error($"Attack ability is not a subroutine for {nameof(Scp1507Role)}"); @@ -84,5 +86,8 @@ public float Damage /// Gets the delay between attacks. /// public float AttackDelay => AttackAbility.AttackDelay; + + /// + public HumeShieldModuleBase HumeShieldModule { get; } } } \ No newline at end of file From e2cd030fc1defb052e7f52dc5d83f965bb91610a Mon Sep 17 00:00:00 2001 From: Yamato Date: Thu, 21 Dec 2023 23:43:58 +0100 Subject: [PATCH 06/10] . --- Exiled.API/Features/Roles/Scp1507Role.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index 5f17198013..1312d6d0fb 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. From 3e92526b19d02cebfd7c4239ca4c3b1b7654841f Mon Sep 17 00:00:00 2001 From: VALERA771 Date: Fri, 22 Dec 2023 14:50:18 +0300 Subject: [PATCH 07/10] lots of new --- Exiled.API/Features/Roles/Scp1507Role.cs | 57 ++++++++++++++- .../EventArgs/Scp1507/ScreamingEventArgs.cs | 40 +++++++++++ Exiled.Events/Handlers/Scp1507.cs | 11 +++ .../Patches/Events/Scp1507/Scream.cs | 71 +++++++++++++++++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs create mode 100644 Exiled.Events/Patches/Events/Scp1507/Scream.cs diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index 1312d6d0fb..b6078802bc 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -7,7 +7,9 @@ namespace Exiled.API.Features.Roles { - using Exiled.API.Enums; + using System.Collections.Generic; + using System.Linq; + using Exiled.API.Interfaces; using PlayerRoles; using PlayerRoles.PlayableScps.HumeShield; @@ -87,7 +89,60 @@ public float Damage /// public float AttackDelay => AttackAbility.AttackDelay; + /// + /// Gets or sets a list with flamingos, which are close to owner. + /// + public IEnumerable NearbyFlamingos + { + get => SwarmAbility._nearbyFlamingos.Select(x => Player.Get(x._lastOwner)); + set + { + SwarmAbility._nearbyFlamingos.Clear(); + + foreach (var player in value.Where(x => x.Role.Is(out _))) + SwarmAbility._nearbyFlamingos.Add(player.Role.As().Base); + } + } + + /// + /// Gets or sets a list with all flamingos. + /// + public IEnumerable AllFlamingos + { + get => SwarmAbility._entireFlock.Select(x => Player.Get(x._lastOwner)); + set + { + SwarmAbility._entireFlock.Clear(); + + foreach (var player in value.Where(x => x.Role.Is(out _))) + SwarmAbility._entireFlock.Add(player.Role.As().Base); + + SwarmAbility._flockSize = (byte)(SwarmAbility._entireFlock.Count - 1); + } + } + + /// + /// Gets or sets a multiplier for healing. + /// + public float Multiplier + { + get => SwarmAbility.Multiplier; + set => SwarmAbility.Multiplier = value; + } + /// public HumeShieldModuleBase HumeShieldModule { get; } + + /// + /// Tries to attack door. + /// + /// if successfully. Otherwise, . + /// This method does not modify game logic, so if you want this method to work correctly, make sure that player is staying in front of the door. + public bool TryAttackDoor() => AttackAbility.TryAttackDoor(); + + /// + /// Forces a SCP-1507 to scream. + /// + public void Scream() => VocalizeAbility.ServerScream(); } } \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs b/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs new file mode 100644 index 0000000000..f0d9f47cfe --- /dev/null +++ b/Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled 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.Events/Handlers/Scp1507.cs b/Exiled.Events/Handlers/Scp1507.cs index 1d79575eca..b1bc8e0e27 100644 --- a/Exiled.Events/Handlers/Scp1507.cs +++ b/Exiled.Events/Handlers/Scp1507.cs @@ -22,10 +22,21 @@ public static class Scp1507 /// public static Event AttackingDoor { get; set; } = new(); + /// + /// Invoked before SCP-1507 screams. + /// + public static Event Screaming { 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); } } \ No newline at end of file diff --git a/Exiled.Events/Patches/Events/Scp1507/Scream.cs b/Exiled.Events/Patches/Events/Scp1507/Scream.cs new file mode 100644 index 0000000000..9fa0efa38b --- /dev/null +++ b/Exiled.Events/Patches/Events/Scp1507/Scream.cs @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled 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); + + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ldarg_0); + + Label retLabel = generator.DefineLabel(); + + newInstructions[newInstructions.Count - 1].labels.Add(retLabel); + + newInstructions.InsertRange( + index, + 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), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file From 210b895be283131d1d6d0bdbc6630957cad4fcc0 Mon Sep 17 00:00:00 2001 From: VALERA771 Date: Fri, 22 Dec 2023 15:10:51 +0300 Subject: [PATCH 08/10] still something new --- Exiled.API/Features/Ragdoll.cs | 6 +- Exiled.API/Features/Scp1507Ragdoll.cs | 74 +++++++++++++++++++ .../Scp049/FinishingRecallEventArgs.cs | 6 ++ .../Patches/Events/Scp049/FinishingRecall.cs | 20 ++++- 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 Exiled.API/Features/Scp1507Ragdoll.cs diff --git a/Exiled.API/Features/Ragdoll.cs b/Exiled.API/Features/Ragdoll.cs index 47c3e10dc2..07d576b437 100644 --- a/Exiled.API/Features/Ragdoll.cs +++ b/Exiled.API/Features/Ragdoll.cs @@ -377,7 +377,11 @@ public static Ragdoll CreateAndSpawn(RoleTypeId roleType, string name, string de /// The to get. /// A or if not found. public static Ragdoll Get(BasicRagdoll ragdoll) => ragdoll == null ? null : - BasicRagdollToRagdoll.TryGetValue(ragdoll, out Ragdoll doll) ? doll : new Ragdoll(ragdoll); + BasicRagdollToRagdoll.TryGetValue(ragdoll, out Ragdoll doll) ? doll : ragdoll switch + { + PlayerRoles.PlayableScps.Scp1507.Scp1507Ragdoll scp1507Ragdoll => new Scp1507Ragdoll(scp1507Ragdoll), + _ => new Ragdoll(ragdoll) + }; /// /// Gets the of belonging to the , if any. diff --git a/Exiled.API/Features/Scp1507Ragdoll.cs b/Exiled.API/Features/Scp1507Ragdoll.cs new file mode 100644 index 0000000000..238d98ee96 --- /dev/null +++ b/Exiled.API/Features/Scp1507Ragdoll.cs @@ -0,0 +1,74 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features +{ + using Exiled.API.Extensions; + using Exiled.API.Features.Roles; + using Exiled.API.Interfaces; + using PlayerRoles; + + using Scp1507BaseRagdoll = PlayerRoles.PlayableScps.Scp1507.Scp1507Ragdoll; + + /// + /// A wrapper for . + /// + public class Scp1507Ragdoll : Ragdoll, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// The encapsulated . + internal Scp1507Ragdoll(Scp1507BaseRagdoll ragdoll) + : base(ragdoll) + { + Base = ragdoll; + } + + /// + public new Scp1507BaseRagdoll Base { get; } + + /// + /// Gets or sets current progress of revival process. + /// + public float RevivalProgress + { + get => Base._revivalProgress; + set => Base.Network_revivalProgress = value; + } + + /// + /// Gets or sets a value indicating whether or not this ragdoll has been revived. + /// + public bool IsRevived + { + get => Base._hasAlreadyRevived; + set => Base.Network_hasAlreadyRevived = value; + } + + /// + /// Gets or sets amount of time when ragdoll was reset last time. + /// + public double ResetTime + { + get => Base._lastResetTime; + set => Base.Network_lastResetTime = value; + } + + /// + /// Spawns a variant from available ragdolls for chosen role. + /// + /// Role. Can be , or . + public void SpawnVariant(RoleTypeId role) => Base.SpawnVariant(role); + + /// + /// Vocalizes ragdoll. + /// + /// Player who vocalizes. If , will be chosen random. + public void Vocalize(Player player = null) => Base.OnVocalize((player ?? Player.Get(x => x.Role.Is(out Scp1507Role _)).GetRandomValue()).ReferenceHub); + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs b/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs index e897dbb04a..2fe4b6ad6b 100644 --- a/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs +++ b/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs @@ -39,6 +39,7 @@ public FinishingRecallEventArgs(Player target, Player scp049, BasicRagdoll ragdo Target = target; Ragdoll = Ragdoll.Get(ragdoll); IsAllowed = isAllowed; + IsFlamingo = ragdoll is PlayerRoles.PlayableScps.Scp1507.Scp1507Ragdoll; } /// @@ -63,5 +64,10 @@ public FinishingRecallEventArgs(Player target, Player scp049, BasicRagdoll ragdo /// Gets or sets a value indicating whether or not the player can be revived. /// public bool IsAllowed { get; set; } + + /// + /// Gets or sets a value indicating whether or not revived player should become a zombie flamingo. + /// + public bool IsFlamingo { get; set; } } } \ No newline at end of file diff --git a/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs b/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs index 2eab40acc5..f11cfb6ebc 100644 --- a/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs +++ b/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs @@ -34,11 +34,13 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - const int offset = -5; + int offset = -5; int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Newobj) + offset; Label returnLabel = generator.DefineLabel(); + LocalBuilder ev = generator.DeclareLocal(typeof(FinishingRecallEventArgs)); + newInstructions.InsertRange( index, new[] @@ -62,6 +64,8 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable x.opcode == OpCodes.Isinst) + offset; + + newInstructions.RemoveRange(index, 3); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // ev.IsFlamingo + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(FinishingRecallEventArgs), nameof(FinishingRecallEventArgs.IsFlamingo))), + }); + newInstructions[newInstructions.Count - 1].WithLabels(returnLabel); for (int z = 0; z < newInstructions.Count; z++) From 4bfafe110eaecd650b7dcb68378ae2623475e29e Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 23 Dec 2023 11:49:24 +0100 Subject: [PATCH 09/10] Update Scp1507Role.cs --- Exiled.API/Features/Roles/Scp1507Role.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index 5c2d796997..e874f71012 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -133,10 +133,7 @@ public float Multiplier get => SwarmAbility.Multiplier; set => SwarmAbility.Multiplier = value; } - - /// - public HumeShieldModuleBase HumeShieldModule { get; } - + /// /// Tries to attack door. /// @@ -149,4 +146,4 @@ public float Multiplier /// public void Scream() => VocalizeAbility.ServerScream(); } -} \ No newline at end of file +} From 8663f11ffd87a32ac6f236e340c62941df8434a4 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 23 Dec 2023 11:56:26 +0100 Subject: [PATCH 10/10] Update Scp1507Role.cs --- Exiled.API/Features/Roles/Scp1507Role.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Exiled.API/Features/Roles/Scp1507Role.cs b/Exiled.API/Features/Roles/Scp1507Role.cs index e874f71012..ac958f9d78 100644 --- a/Exiled.API/Features/Roles/Scp1507Role.cs +++ b/Exiled.API/Features/Roles/Scp1507Role.cs @@ -133,7 +133,7 @@ public float Multiplier get => SwarmAbility.Multiplier; set => SwarmAbility.Multiplier = value; } - + /// /// Tries to attack door. ///