Skip to content

Commit

Permalink
More features and events for SCP-1507 (#2348)
Browse files Browse the repository at this point in the history
* scp-1507

* minor changes

* added role

* new

* IHumeShieldRole

* .

* lots of new

* still something new

* Update Scp1507Role.cs

* Update Scp1507Role.cs

---------

Co-authored-by: Yamato <louismonneyron5@yahoo.com>
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 23, 2023
1 parent b095edc commit c7cf955
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 5 deletions.
6 changes: 5 additions & 1 deletion Exiled.API/Features/Ragdoll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,11 @@ public static Ragdoll CreateAndSpawn(RoleTypeId roleType, string name, string de
/// <param name="ragdoll">The <see cref="BasicRagdoll"/> to get.</param>
/// <returns>A <see cref="Ragdoll"/> or <see langword="null"/> if not found.</returns>
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)
};

/// <summary>
/// Gets the <see cref="IEnumerable{T}"/> of <see cref="Ragdoll"/> belonging to the <see cref="Player"/>, if any.
Expand Down
62 changes: 59 additions & 3 deletions Exiled.API/Features/Roles/Scp1507Role.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Exiled.API.Features.Roles
{
using System.Collections.Generic;
using System.Linq;

using Exiled.API.Enums;
using Exiled.API.Interfaces;
using PlayerRoles;
Expand Down Expand Up @@ -58,6 +61,9 @@ public Scp1507Role(BaseRole baseRole)
/// <inheritdoc/>
public SubroutineManagerModule SubroutineModule { get; }

/// <inheritdoc/>
public HumeShieldModuleBase HumeShieldModule { get; }

/// <summary>
/// Gets the <see cref="Scp1507AttackAbility"/> for this role.
/// </summary>
Expand Down Expand Up @@ -87,7 +93,57 @@ public float Damage
/// </summary>
public float AttackDelay => AttackAbility.AttackDelay;

/// <inheritdoc/>
public HumeShieldModuleBase HumeShieldModule { get; }
/// <summary>
/// Gets or sets a list with flamingos, which are close to owner.
/// </summary>
public IEnumerable<Player> NearbyFlamingos
{
get => SwarmAbility._nearbyFlamingos.Select(x => Player.Get(x._lastOwner));
set
{
SwarmAbility._nearbyFlamingos.Clear();

foreach (var player in value.Where(x => x.Role.Is<Scp1507Role>(out _)))
SwarmAbility._nearbyFlamingos.Add(player.Role.As<Scp1507Role>().Base);
}
}

/// <summary>
/// Gets or sets a list with all flamingos.
/// </summary>
public IEnumerable<Player> AllFlamingos
{
get => SwarmAbility._entireFlock.Select(x => Player.Get(x._lastOwner));
set
{
SwarmAbility._entireFlock.Clear();

foreach (var player in value.Where(x => x.Role.Is<Scp1507Role>(out _)))
SwarmAbility._entireFlock.Add(player.Role.As<Scp1507Role>().Base);

SwarmAbility._flockSize = (byte)(SwarmAbility._entireFlock.Count - 1);
}
}

/// <summary>
/// Gets or sets a multiplier for healing.
/// </summary>
public float Multiplier
{
get => SwarmAbility.Multiplier;
set => SwarmAbility.Multiplier = value;
}

/// <summary>
/// Tries to attack door.
/// </summary>
/// <returns><see langword="true"/> if successfully. Otherwise, <see langword="false"/>.</returns>
/// <remarks>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.</remarks>
public bool TryAttackDoor() => AttackAbility.TryAttackDoor();

/// <summary>
/// Forces a SCP-1507 to scream.
/// </summary>
public void Scream() => VocalizeAbility.ServerScream();
}
}
}
74 changes: 74 additions & 0 deletions Exiled.API/Features/Scp1507Ragdoll.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// -----------------------------------------------------------------------
// <copyright file="Scp1507Ragdoll.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

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;

/// <summary>
/// A wrapper for <see cref="Scp1507BaseRagdoll"/>.
/// </summary>
public class Scp1507Ragdoll : Ragdoll, IWrapper<Scp1507BaseRagdoll>
{
/// <summary>
/// Initializes a new instance of the <see cref="Scp1507Ragdoll"/> class.
/// </summary>
/// <param name="ragdoll">The encapsulated <see cref="Scp1507BaseRagdoll"/>.</param>
internal Scp1507Ragdoll(Scp1507BaseRagdoll ragdoll)
: base(ragdoll)
{
Base = ragdoll;
}

/// <inheritdoc/>
public new Scp1507BaseRagdoll Base { get; }

/// <summary>
/// Gets or sets current progress of revival process.
/// </summary>
public float RevivalProgress
{
get => Base._revivalProgress;
set => Base.Network_revivalProgress = value;
}

/// <summary>
/// Gets or sets a value indicating whether or not this ragdoll has been revived.
/// </summary>
public bool IsRevived
{
get => Base._hasAlreadyRevived;
set => Base.Network_hasAlreadyRevived = value;
}

/// <summary>
/// Gets or sets amount of time when ragdoll was reset last time.
/// </summary>
public double ResetTime
{
get => Base._lastResetTime;
set => Base.Network_lastResetTime = value;
}

/// <summary>
/// Spawns a variant from available ragdolls for chosen role.
/// </summary>
/// <param name="role">Role. Can be <see cref="RoleTypeId.Flamingo"/>, <see cref="RoleTypeId.AlphaFlamingo"/> or <see cref="RoleTypeId.ZombieFlamingo"/>.</param>
public void SpawnVariant(RoleTypeId role) => Base.SpawnVariant(role);

/// <summary>
/// Vocalizes ragdoll.
/// </summary>
/// <param name="player">Player who vocalizes. If <see langword="null"/>, will be chosen random.</param>
public void Vocalize(Player player = null) => Base.OnVocalize((player ?? Player.Get(x => x.Role.Is(out Scp1507Role _)).GetRandomValue()).ReferenceHub);
}
}
6 changes: 6 additions & 0 deletions Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/// <inheritdoc/>
Expand All @@ -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.
/// </summary>
public bool IsAllowed { get; set; }

/// <summary>
/// Gets or sets a value indicating whether or not revived player should become a zombie flamingo.
/// </summary>
public bool IsFlamingo { get; set; }
}
}
40 changes: 40 additions & 0 deletions Exiled.Events/EventArgs/Scp1507/ScreamingEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// -----------------------------------------------------------------------
// <copyright file="ScreamingEventArgs.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Scp1507
{
using Exiled.API.Features;
using Exiled.API.Features.Roles;
using Exiled.Events.EventArgs.Interfaces;

/// <summary>
/// Contains all information before SCP-1507 screams.
/// </summary>
public class ScreamingEventArgs : IScp1507Event, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ScreamingEventArgs"/> class.
/// </summary>
/// <param name="player"><inheritdoc cref="Player"/></param>
/// <param name="isAllowed"><inheritdoc cref="IsAllowed"/></param>
public ScreamingEventArgs(Player player, bool isAllowed = true)
{
Player = player;
Scp1507 = player.Role.As<Scp1507Role>();
IsAllowed = isAllowed;
}

/// <inheritdoc/>
public Player Player { get; }

/// <inheritdoc/>
public Scp1507Role Scp1507 { get; }

/// <inheritdoc/>
public bool IsAllowed { get; set; }
}
}
11 changes: 11 additions & 0 deletions Exiled.Events/Handlers/Scp1507.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,21 @@ public static class Scp1507
/// </summary>
public static Event<AttackingDoorEventArgs> AttackingDoor { get; set; } = new();

/// <summary>
/// Invoked before SCP-1507 screams.
/// </summary>
public static Event<ScreamingEventArgs> Screaming { get; set; } = new();

/// <summary>
/// Called before SCP-1507 attacks door.
/// </summary>
/// <param name="ev">The <see cref="AttackingDoorEventArgs"/> instance.</param>
public static void OnAttackingDoor(AttackingDoorEventArgs ev) => AttackingDoor.InvokeSafely(ev);

/// <summary>
/// Called before SCP-1507 screams.
/// </summary>
/// <param name="ev">The <see cref="ScreamingEventArgs"/> instance.</param>
public static void OnScreaming(ScreamingEventArgs ev) => Screaming.InvokeSafely(ev);
}
}
20 changes: 19 additions & 1 deletion Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.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[]
Expand All @@ -62,6 +64,8 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi
// FinishingRecallEventArgs ev = new(target, scp049, BasicRagdoll, bool)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(FinishingRecallEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, ev.LocalIndex),

// Handlers.Scp049.OnFinishingRecall(ev)
new(OpCodes.Call, Method(typeof(Handlers.Scp049), nameof(Handlers.Scp049.OnFinishingRecall))),
Expand All @@ -72,6 +76,20 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi
new(OpCodes.Brfalse_S, returnLabel),
});

offset = -2;
index = newInstructions.FindIndex(x => 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++)
Expand Down
71 changes: 71 additions & 0 deletions Exiled.Events/Patches/Events/Scp1507/Scream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// -----------------------------------------------------------------------
// <copyright file="Scream.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

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;

/// <summary>
/// Patches <see cref="Scp1507VocalizeAbility.ServerProcessCmd"/>
/// to add <see cref="Handlers.Scp1507.Screaming"/> event.
/// </summary>
[EventPatch(typeof(Handlers.Scp1507), nameof(Handlers.Scp1507.Screaming))]
[HarmonyPatch(typeof(Scp1507VocalizeAbility), nameof(Scp1507VocalizeAbility.ServerProcessCmd))]
public class Scream
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.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<CodeInstruction>.Pool.Return(newInstructions);
}
}
}

0 comments on commit c7cf955

Please sign in to comment.