diff --git a/.github/labeler.yml b/.github/labeler.yml
index 5098459b0..97048dd23 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -35,6 +35,6 @@ Localization: # Add the 'Localization' label
- changed-files:
- any-glob-to-any-file: EXILED/Localization/** # Any modifications to Localization
-GitHub_Actions: # Add the 'GitHub' label
+GitHub: # Add the 'GitHub' label
- changed-files:
- any-glob-to-any-file: .github/** # Any modifications to github related files
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 000000000..032dc22c6
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,35 @@
+## Description
+**Describe the changes**
+
+
+**What is the current behavior?** (You can also link to an open issue here)
+
+
+**What is the new behavior?** (if this is a feature change)
+
+
+**Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
+
+
+**Other information**:
+
+
+
+## Types of changes
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to change)
+- [ ] Documentations
+
+
+## Submission checklist
+
+- [ ] I have checked the project can be compiled
+- [ ] I have tested my changes and it worked as expected
+
+### Patches (if there are any changes related to Harmony patches)
+- [ ] I have checked no IL patching errors in the console
+
+### Other
+- [ ] Still requires more testing
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 351cdd3be..df73ad9a5 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -12,7 +12,7 @@ permissions:
id-token: write
env:
- EXILED_REFERENCES_URL: https://Exiled-Official.github.io/SL-References/Dev.zip
+ EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Master.zip
EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7be77c41a..6f9bc0f85 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,7 +13,7 @@ defaults:
working-directory: ./EXILED
env:
- EXILED_REFERENCES_URL: https://Exiled-Official.github.io/SL-References/Master.zip
+ EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Master.zip
EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References
jobs:
diff --git a/.github/workflows/labeler.yml b/.github/workflows/pull_request_opened.yml
similarity index 72%
rename from .github/workflows/labeler.yml
rename to .github/workflows/pull_request_opened.yml
index 0486bfaec..a54bed971 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/pull_request_opened.yml
@@ -20,3 +20,10 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
sync-labels: true
+ assign-author:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+
+ steps:
+ - uses: toshimaru/auto-author-assign@v2.1.1
diff --git a/.github/workflows/push_nuget.yml b/.github/workflows/push_nuget.yml
index 72e45189e..3e5cf7c46 100644
--- a/.github/workflows/push_nuget.yml
+++ b/.github/workflows/push_nuget.yml
@@ -10,7 +10,7 @@ defaults:
working-directory: ./EXILED
env:
- EXILED_REFERENCES_URL: https://Exiled-Official.github.io/SL-References/Master.zip
+ EXILED_REFERENCES_URL: https://ExMod-Team.github.io/SL-References/Master.zip
EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References
jobs:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bcbe907cc..efdac4cc8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,9 +11,9 @@ defaults:
working-directory: ./EXILED
env:
- EXILED_REFERENCES_URL: https://Exiled-Official.github.io/SL-References/Dev.zip
+ EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Dev.zip
EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References
- EXILED_DLL_ARCHIVER_URL: https://github.com/Exiled-Official/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe
+ EXILED_DLL_ARCHIVER_URL: https://github.com/ExMod-Team/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe
jobs:
build:
diff --git a/EXILED/Exiled.API/Enums/AuthenticationType.cs b/EXILED/Exiled.API/Enums/AuthenticationType.cs
index 9de49f902..0590ea097 100644
--- a/EXILED/Exiled.API/Enums/AuthenticationType.cs
+++ b/EXILED/Exiled.API/Enums/AuthenticationType.cs
@@ -42,5 +42,10 @@ public enum AuthenticationType
/// Indicates that the player has been authenticated as DedicatedServer.
///
DedicatedServer,
+
+ ///
+ /// Indicates that the player has been authenticated during Offline mode.
+ ///
+ Offline,
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Enums/HazardType.cs b/EXILED/Exiled.API/Enums/HazardType.cs
new file mode 100644
index 000000000..f05f8852f
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/HazardType.cs
@@ -0,0 +1,37 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ using Exiled.API.Features.Hazards;
+
+ ///
+ /// Unique identifier for a .
+ ///
+ public enum HazardType
+ {
+ ///
+ /// SCP-939 amnestic cloud.
+ ///
+ AmnesticCloud,
+
+ ///
+ /// Sinkhole spawned at start of round.
+ ///
+ Sinkhole,
+
+ ///
+ /// SCP-173 tantrum.
+ ///
+ Tantrum,
+
+ ///
+ /// Should never happen
+ ///
+ Unknown,
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Enums/UncuffReason.cs b/EXILED/Exiled.API/Enums/UncuffReason.cs
new file mode 100644
index 000000000..6b3018fda
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/UncuffReason.cs
@@ -0,0 +1,30 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ ///
+ /// Reasons that player gets uncuffed.
+ ///
+ public enum UncuffReason
+ {
+ ///
+ /// Uncuffed by a player.
+ ///
+ Player,
+
+ ///
+ /// Uncuffed due to the distance between cuffer and target.
+ ///
+ OutOfRange,
+
+ ///
+ /// Uncuffed due to the cuffer no longer alive.
+ ///
+ CufferDied,
+ }
+}
diff --git a/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
new file mode 100644
index 000000000..2f8473784
--- /dev/null
+++ b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
@@ -0,0 +1,63 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Extensions
+{
+ using System;
+
+ ///
+ /// Extensions for bitwise operations.
+ ///
+ public static class BitwiseExtensions
+ {
+ ///
+ /// Adds the specified flags to the given enum value.
+ ///
+ /// The type of the enum.
+ /// The enum value to add flags to.
+ /// The flags to add.
+ /// The enum value with the specified flags added.
+ public static T AddFlags(this T flags, params T[] newFlags)
+ where T : Enum => flags.ModifyFlags(true, newFlags);
+
+ ///
+ /// Removes the specified flags from the given enum value.
+ ///
+ /// The type of the enum.
+ /// The enum value to remove flags from.
+ /// The flags to remove.
+ /// The enum value with the specified flags removed.
+ public static T RemoveFlags(this T flags, params T[] oldFlags)
+ where T : Enum => flags.ModifyFlags(false, oldFlags);
+
+ ///
+ /// Sets the specified flag to the given value, default is true.
+ ///
+ /// The flags enum to modify.
+ /// The value to set the flag to.
+ /// The flags to modify.
+ /// The type of the enum.
+ /// The flags enum with the flag set to the given value.
+ public static T ModifyFlags(this T flags, bool value, params T[] changeFlags)
+ where T : Enum
+ {
+ long currentValue = Convert.ToInt64(flags);
+
+ foreach (T flag in changeFlags)
+ {
+ long flagValue = Convert.ToInt64(flag);
+
+ if (value)
+ currentValue |= flagValue;
+ else
+ currentValue &= ~flagValue;
+ }
+
+ return (T)Enum.ToObject(typeof(T), currentValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index 21fe13f72..d2afe9487 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -25,6 +25,7 @@ namespace Exiled.API.Extensions
using PlayerRoles;
using PlayerRoles.FirstPersonControl;
using PlayerRoles.PlayableScps.Scp049.Zombies;
+ using PlayerRoles.Voice;
using RelativePositioning;
using Respawning;
@@ -181,6 +182,18 @@ public static void PlayGunSound(this Player player, Vector3 position, ItemType i
player.Connection.Send(message);
}
+ ///
+ /// Sets that only the player can see.
+ ///
+ /// Only this player can see Display Text.
+ /// Text displayed to the player.
+ public static void SetIntercomDisplayTextForTargetOnly(this Player target, string text) => target.SendFakeSyncVar(IntercomDisplay._singleton.netIdentity, typeof(IntercomDisplay), nameof(IntercomDisplay.Network_overrideText), text);
+
+ ///
+ /// Resync .
+ ///
+ public static void ResetIntercomDisplayText() => ResyncSyncVar(IntercomDisplay._singleton.netIdentity, typeof(IntercomDisplay), nameof(IntercomDisplay.Network_overrideText));
+
///
/// Sets of a that only the player can see.
///
@@ -460,12 +473,11 @@ public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwne
///
/// EffectOnlySCP207.
///
- /// MirrorExtensions.SendCustomSync(player, player.ReferenceHub.networkIdentity, typeof(PlayerEffectsController), (writer) => {
- /// writer.WriteUInt64(1ul); // DirtyObjectsBit
- /// writer.WriteUInt32(1); // DirtyIndexCount
+ /// MirrorExtensions.SendFakeSyncObject(player, player.NetworkIdentity, typeof(PlayerEffectsController), (writer) => {
+ /// writer.WriteULong(1ul); // DirtyObjectsBit
+ /// writer.WriteUInt(1); // DirtyIndexCount
/// writer.WriteByte((byte)SyncList<byte>.Operation.OP_SET); // Operations
- /// writer.WriteUInt32(17); // EditIndex
- /// writer.WriteByte(1); // Value
+ /// writer.WriteUInt(17); // EditIndex
/// });
///
///
diff --git a/EXILED/Exiled.API/Extensions/RoleExtensions.cs b/EXILED/Exiled.API/Extensions/RoleExtensions.cs
index e9186ba71..c674af1bf 100644
--- a/EXILED/Exiled.API/Extensions/RoleExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/RoleExtensions.cs
@@ -140,18 +140,22 @@ public static SpawnLocation GetRandomSpawnLocation(this RoleTypeId roleType)
return null;
}
+ ///
+ /// Gets the starting of a .
+ ///
+ /// The .
+ /// The that the role receives on spawn.
+ public static InventoryRoleInfo GetInventory(this RoleTypeId role)
+ => StartingInventories.DefinedInventories.TryGetValue(role, out InventoryRoleInfo info)
+ ? info
+ : new(Array.Empty(), new());
+
///
/// Gets the starting items of a .
///
/// The .
/// An of that the role receives on spawn. Will be empty for classes that do not spawn with items.
- public static ItemType[] GetStartingInventory(this RoleTypeId roleType)
- {
- if (StartingInventories.DefinedInventories.TryGetValue(roleType, out InventoryRoleInfo info))
- return info.Items;
-
- return Array.Empty();
- }
+ public static ItemType[] GetStartingInventory(this RoleTypeId roleType) => GetInventory(roleType).Items;
///
/// Gets the starting ammo of a .
@@ -160,10 +164,9 @@ public static ItemType[] GetStartingInventory(this RoleTypeId roleType)
/// An of that the role receives on spawn. Will be empty for classes that do not spawn with ammo.
public static Dictionary GetStartingAmmo(this RoleTypeId roleType)
{
- if (StartingInventories.DefinedInventories.TryGetValue(roleType, out InventoryRoleInfo info))
- return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
+ InventoryRoleInfo info = roleType.GetInventory();
- return new();
+ return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
}
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/StringExtensions.cs b/EXILED/Exiled.API/Extensions/StringExtensions.cs
index 69b184bc6..37d1a6777 100644
--- a/EXILED/Exiled.API/Extensions/StringExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/StringExtensions.cs
@@ -161,7 +161,11 @@ public static string GetBefore(this string input, char symbol)
///
/// The user id.
/// Returns the raw user id.
- public static string GetRawUserId(this string userId) => userId.Substring(0, userId.LastIndexOf('@'));
+ public static string GetRawUserId(this string userId)
+ {
+ int index = userId.IndexOf('@');
+ return index == -1 ? userId : userId.Substring(0, index);
+ }
///
/// Gets a SHA256 hash of a player's user id without the authentication.
diff --git a/EXILED/Exiled.API/Features/Camera.cs b/EXILED/Exiled.API/Features/Camera.cs
index 67f5d7418..56e7e7f24 100644
--- a/EXILED/Exiled.API/Features/Camera.cs
+++ b/EXILED/Exiled.API/Features/Camera.cs
@@ -28,7 +28,7 @@ public class Camera : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary Camera079ToCamera = new(250);
+ internal static readonly Dictionary Camera079ToCamera = new(250, new ComponentsEqualityComparer());
private static readonly Dictionary NameToCameraType = new()
{
diff --git a/EXILED/Exiled.API/Features/Doors/AirlockController.cs b/EXILED/Exiled.API/Features/Doors/AirlockController.cs
index 62645a392..eceda5f31 100644
--- a/EXILED/Exiled.API/Features/Doors/AirlockController.cs
+++ b/EXILED/Exiled.API/Features/Doors/AirlockController.cs
@@ -20,7 +20,7 @@ public class AirlockController
///
/// A containing all known 's and their corresponding .
///
- internal static readonly Dictionary BaseToExiledControllers = new();
+ internal static readonly Dictionary BaseToExiledControllers = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs
index 584dc3fb0..c06a13b9b 100644
--- a/EXILED/Exiled.API/Features/Doors/Door.cs
+++ b/EXILED/Exiled.API/Features/Doors/Door.cs
@@ -14,7 +14,9 @@ namespace Exiled.API.Features.Doors
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Core;
+ using Exiled.API.Features.Hazards;
using Exiled.API.Interfaces;
+ using global::Hazards;
using Interactables.Interobjects;
using Interactables.Interobjects.DoorUtils;
using MEC;
@@ -38,7 +40,7 @@ public class Door : TypeCastObject, IWrapper, IWorldSpace
///
/// A containing all known 's and their corresponding .
///
- internal static readonly Dictionary DoorVariantToDoor = new();
+ internal static readonly Dictionary DoorVariantToDoor = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -309,6 +311,31 @@ public static Door Get(DoorVariant doorVariant)
return DoorVariantToDoor[doorVariant];
}
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an door.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(DoorVariant doorVariant)
+ where T : Door => Get(doorVariant) as T;
+
+ ///
+ /// Gets a given the specified .
+ ///
+ /// The to search for.
+ /// The with the given or if not found.
+ public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType);
+
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an door.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(DoorType doorType)
+ where T : Door => Get(doorType) as T;
+
///
/// Gets a given the specified name.
///
@@ -320,6 +347,15 @@ public static Door Get(string name)
return nameExtension is null ? null : Get(nameExtension.TargetDoor);
}
+ ///
+ /// Gets the by .
+ ///
+ /// The name to search for.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(string name)
+ where T : Door => Get(name) as T;
+
///
/// Gets the door object associated with a specific , or creates a new one if there isn't one.
///
@@ -327,20 +363,6 @@ public static Door Get(string name)
/// The with the given name or if not found.
public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInChildren());
- ///
- /// Gets a of filtered based on a predicate.
- ///
- /// The condition to satify.
- /// A of which contains elements that satify the condition.
- public static IEnumerable Get(Func predicate) => List.Where(predicate);
-
- ///
- /// Gets a given the specified .
- ///
- /// The to search for.
- /// The with the given or if not found.
- public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType);
-
///
/// Returns the closest to the given .
///
@@ -367,6 +389,13 @@ public static Door Random(ZoneType type = ZoneType.Unspecified, bool onlyUnbroke
return doors[UnityEngine.Random.Range(0, doors.Count)];
}
+ ///
+ /// Gets a of filtered based on a predicate.
+ ///
+ /// The condition to satify.
+ /// A of which contains elements that satify the condition.
+ public static IEnumerable Get(Func predicate) => List.Where(predicate);
+
///
/// Locks all doors given the specified .
///
diff --git a/EXILED/Exiled.API/Features/Generator.cs b/EXILED/Exiled.API/Features/Generator.cs
index b3f5bfb69..315f9af0f 100644
--- a/EXILED/Exiled.API/Features/Generator.cs
+++ b/EXILED/Exiled.API/Features/Generator.cs
@@ -26,7 +26,7 @@ public class Generator : IWrapper, IWorldSpace
///
/// A of on the map.
///
- internal static readonly Dictionary Scp079GeneratorToGenerator = new();
+ internal static readonly Dictionary Scp079GeneratorToGenerator = new(new ComponentsEqualityComparer());
private Room room;
///
diff --git a/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs b/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
index c385c143f..31c9ad244 100644
--- a/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
@@ -3,10 +3,11 @@
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
//
-// -----------------------------------------------------------------------
+// ------------------------------------------------------------------------
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using PlayerRoles.PlayableScps.Scp939;
///
@@ -14,6 +15,8 @@ namespace Exiled.API.Features.Hazards
///
public class AmnesticCloudHazard : TemporaryHazard
{
+ private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
+
///
/// Initializes a new instance of the class.
///
@@ -26,9 +29,26 @@ public AmnesticCloudHazard(Scp939AmnesticCloudInstance hazard)
Owner = Player.Get(Ability.Owner);
}
+ ///
+ /// Gets the amnestic cloud prefab.
+ ///
+ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
+ {
+ get
+ {
+ if (amnesticCloudPrefab == null)
+ amnesticCloudPrefab = PrefabHelper.GetPrefab(PrefabType.AmnesticCloudHazard);
+
+ return amnesticCloudPrefab;
+ }
+ }
+
///
public new Scp939AmnesticCloudInstance Base { get; }
+ ///
+ public override HazardType Type => HazardType.AmnesticCloud;
+
///
/// Gets the for this instance.
///
diff --git a/EXILED/Exiled.API/Features/Hazards/Hazard.cs b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
index 6861d1a68..09bd880d3 100644
--- a/EXILED/Exiled.API/Features/Hazards/Hazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Hazards
using System.Collections.Generic;
using System.Linq;
+ using Exiled.API.Enums;
using Exiled.API.Features.Core;
using Exiled.API.Interfaces;
using global::Hazards;
@@ -25,7 +26,7 @@ public class Hazard : TypeCastObject, IWrapper
///
/// with to it's .
///
- internal static readonly Dictionary EnvironmentalHazardToHazard = new();
+ internal static readonly Dictionary EnvironmentalHazardToHazard = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -48,6 +49,11 @@ public Hazard(EnvironmentalHazard hazard)
///
public EnvironmentalHazard Base { get; }
+ ///
+ /// Gets the associated with the current Hazard.
+ ///
+ public virtual HazardType Type { get; } = HazardType.Unknown;
+
///
/// Gets or sets the list with all affected by this hazard players.
///
@@ -123,6 +129,15 @@ public static Hazard Get(EnvironmentalHazard environmentalHazard) =>
_ => new Hazard(environmentalHazard)
};
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an hazard.
+ /// The specified type.
+ /// The hazard wrapper for the given .
+ public static T Get(EnvironmentalHazard environmentalHazard)
+ where T : Hazard => Get(environmentalHazard) as T;
+
///
/// Gets the hazard by the room where it's located.
///
@@ -144,6 +159,13 @@ public static Hazard Get(EnvironmentalHazard environmentalHazard) =>
/// of based on predicate.
public static IEnumerable Get(Func predicate) => List.Where(predicate);
+ ///
+ /// Gets an of .
+ ///
+ /// The to get.
+ /// of based on type.
+ public static IEnumerable Get(HazardType type) => Get(h => h.Type == type);
+
///
/// Checks if player is in hazard zone.
///
diff --git a/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs b/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
index e8e0c4f3a..34cbaacd5 100644
--- a/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
@@ -7,6 +7,7 @@
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using global::Hazards;
///
@@ -28,5 +29,8 @@ public SinkholeHazard(SinkholeEnvironmentalHazard hazard)
/// Gets the .
///
public new SinkholeEnvironmentalHazard Base { get; }
+
+ ///
+ public override HazardType Type => HazardType.Sinkhole;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs b/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
index 15f54b5ac..906bd4faf 100644
--- a/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
@@ -7,7 +7,9 @@
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using global::Hazards;
+ using Mirror;
using RelativePositioning;
using UnityEngine;
@@ -16,6 +18,8 @@ namespace Exiled.API.Features.Hazards
///
public class TantrumHazard : TemporaryHazard
{
+ private static TantrumEnvironmentalHazard tantrumPrefab;
+
///
/// Initializes a new instance of the class.
///
@@ -26,11 +30,28 @@ public TantrumHazard(TantrumEnvironmentalHazard hazard)
Base = hazard;
}
+ ///
+ /// Gets the tantrum prefab.
+ ///
+ public static TantrumEnvironmentalHazard TantrumPrefab
+ {
+ get
+ {
+ if (tantrumPrefab == null)
+ tantrumPrefab = PrefabHelper.GetPrefab(PrefabType.TantrumObj);
+
+ return tantrumPrefab;
+ }
+ }
+
///
/// Gets the .
///
public new TantrumEnvironmentalHazard Base { get; }
+ ///
+ public override HazardType Type => HazardType.Tantrum;
+
///
/// Gets or sets a value indicating whether or not sizzle should be played.
///
@@ -57,5 +78,28 @@ public Transform CorrectPosition
get => Base._correctPosition;
set => Base._correctPosition = value;
}
+
+ ///
+ /// Places a Tantrum (SCP-173's ability) in the indicated position.
+ ///
+ /// The position where you want to spawn the Tantrum.
+ /// Whether or not the tantrum will apply the effect.
+ /// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
+ /// The instance.
+ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
+ {
+ TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
+
+ if (!isActive)
+ tantrum.SynchronizedPosition = new(position);
+ else
+ tantrum.SynchronizedPosition = new(position + (Vector3.up * 0.25f));
+
+ tantrum._destroyed = !isActive;
+
+ NetworkServer.Spawn(tantrum.gameObject);
+
+ return Get(tantrum);
+ }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Items/Ammo.cs b/EXILED/Exiled.API/Features/Items/Ammo.cs
index 4c88a7f84..64c598ad7 100644
--- a/EXILED/Exiled.API/Features/Items/Ammo.cs
+++ b/EXILED/Exiled.API/Features/Items/Ammo.cs
@@ -20,7 +20,7 @@ public class Ammo : Item, IWrapper
///
/// Gets the absolute maximum amount of ammo that may be held at one time, if ammo is forcefully given to the player (regardless of worn armor or server configuration).
///
- /// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see .
+ /// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see .
///
///
public const ushort AmmoLimit = ushort.MaxValue;
diff --git a/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs b/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
index 8f9aa165c..0002d5d76 100644
--- a/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
+++ b/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
@@ -119,7 +119,7 @@ public ExplosionGrenadeProjectile SpawnActive(Vector3 position, Player owner = n
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- ExplosionGrenadeProjectile grenade = (ExplosionGrenadeProjectile)Pickup.Get(ipb);
+ ExplosionGrenadeProjectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/FlashGrenade.cs b/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
index e2d009ac7..979784f2c 100644
--- a/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
+++ b/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
@@ -100,7 +100,7 @@ public FlashbangProjectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- FlashbangProjectile grenade = (FlashbangProjectile)Pickup.Get(ipb);
+ FlashbangProjectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs
index fd0bba35a..96d02257f 100644
--- a/EXILED/Exiled.API/Features/Items/Item.cs
+++ b/EXILED/Exiled.API/Features/Items/Item.cs
@@ -25,7 +25,6 @@ namespace Exiled.API.Features.Items
using InventorySystem.Items.Radio;
using InventorySystem.Items.ThrowableProjectiles;
using InventorySystem.Items.ToggleableLights;
- using InventorySystem.Items.ToggleableLights.Flashlight;
using InventorySystem.Items.Usables;
using InventorySystem.Items.Usables.Scp1576;
using InventorySystem.Items.Usables.Scp244;
@@ -33,7 +32,6 @@ namespace Exiled.API.Features.Items
using UnityEngine;
using BaseConsumable = InventorySystem.Items.Usables.Consumable;
- using Object = UnityEngine.Object;
///
/// A wrapper class for .
@@ -43,7 +41,7 @@ public class Item : TypeCastObject, IWrapper
///
/// A dictionary of all 's that have been converted into .
///
- internal static readonly Dictionary BaseToItem = new();
+ internal static readonly Dictionary BaseToItem = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -219,6 +217,15 @@ public static Item Get(ItemBase itemBase)
};
}
+ ///
+ /// Gets an existing or creates a new instance of one.
+ ///
+ /// The to convert into an item.
+ /// The specified type.
+ /// The item wrapper for the given .
+ public static T Get(ItemBase itemBase)
+ where T : Item => Get(itemBase) as T;
+
///
/// Gets the Item belonging to the specified serial.
///
@@ -279,6 +286,40 @@ ItemType.KeycardGuard or ItemType.KeycardJanitor or ItemType.KeycardO5 or ItemTy
_ => new Item(type),
};
+ ///
+ /// Creates a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be casted into a subclass to gain more control over the object.
+ /// - Usable items (Adrenaline, Medkit, Painkillers, SCP-207, SCP-268, and SCP-500) should be casted to the class.
+ /// - All valid ammo should be casted to the class.
+ /// - All valid firearms (not including the Micro HID) should be casted to the class.
+ /// - All valid keycards should be casted to the class.
+ /// - All valid armor should be casted to the class.
+ /// - Explosive grenades and SCP-018 should be casted to the class.
+ /// - Flash grenades should be casted to the class.
+ ///
+ ///
+ /// The following have their own respective classes:
+ /// - Flashlights can be casted to .
+ /// - Radios can be casted to .
+ /// - The Micro HID can be casted to .
+ /// - SCP-244 A and B variants can be casted to .
+ /// - SCP-330 can be casted to .
+ /// - SCP-2176 can be casted to the class.
+ /// - SCP-1576 can be casted to the class.
+ /// - Jailbird can be casted to the class.
+ ///
+ ///
+ /// Items that are not listed above do not have a subclass, and can only use the base class.
+ ///
+ ///
+ /// The of the item to create.
+ /// The who owns the item by default.
+ /// The specified type.
+ /// The created. This can be cast as a subclass.
+ public static Item Create(ItemType type, Player owner = null)
+ where T : Item => Create(type, owner) as T;
+
///
/// Gives this item to a .
///
diff --git a/EXILED/Exiled.API/Features/Items/Jailbird.cs b/EXILED/Exiled.API/Features/Items/Jailbird.cs
index 9e508da11..2f8bfb524 100644
--- a/EXILED/Exiled.API/Features/Items/Jailbird.cs
+++ b/EXILED/Exiled.API/Features/Items/Jailbird.cs
@@ -7,11 +7,14 @@
namespace Exiled.API.Features.Items
{
+ using System;
+
using Exiled.API.Features.Pickups;
using Exiled.API.Interfaces;
using InventorySystem.Items.Autosync;
using InventorySystem.Items.Jailbird;
using Mirror;
+ using UnityEngine;
using JailbirdPickup = Pickups.JailbirdPickup;
@@ -114,12 +117,35 @@ public JailbirdWearState WearState
get => Base._deterioration.WearState;
set
{
- if (JailbirdDeteriorationTracker.ReceivedStates.ContainsKey(Serial))
- JailbirdDeteriorationTracker.ReceivedStates[Serial] = value;
+ TotalDamageDealt = GetDamage(value);
+ TotalCharges = GetCharge(value);
Base._deterioration.RecheckUsage();
}
}
+ ///
+ /// Calculates the damage corresponding to a given .
+ ///
+ /// The wear state to calculate damage for.
+ /// The amount of damage associated with the specified wear state.
+ public float GetDamage(JailbirdWearState wearState)
+ {
+ foreach (Keyframe keyframe in Base._deterioration._damageToWearState.keys)
+ {
+ if (Base._deterioration.FloatToState(keyframe.value) == wearState)
+ return keyframe.time;
+ }
+
+ throw new Exception("Wear state not found in damage to wear state mapping.");
+ }
+
+ ///
+ /// Gets the charge needed to reach a specific .
+ ///
+ /// The desired wear state to calculate the charge for.
+ /// The charge value required to achieve the specified wear state.
+ public int GetCharge(JailbirdWearState wearState) => (int)wearState;
+
///
/// Breaks the Jailbird.
///
diff --git a/EXILED/Exiled.API/Features/Items/Scp018.cs b/EXILED/Exiled.API/Features/Items/Scp018.cs
index 57f8122c1..7c08c7c6d 100644
--- a/EXILED/Exiled.API/Features/Items/Scp018.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp018.cs
@@ -86,7 +86,7 @@ public Scp018Projectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp018Projectile grenade = (Scp018Projectile)Pickup.Get(ipb);
+ Scp018Projectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Scp2176.cs b/EXILED/Exiled.API/Features/Items/Scp2176.cs
index 412c371d6..0ca9fc54d 100644
--- a/EXILED/Exiled.API/Features/Items/Scp2176.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp2176.cs
@@ -71,7 +71,7 @@ public Scp2176Projectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp2176Projectile grenade = (Scp2176Projectile)Pickup.Get(ipb);
+ Scp2176Projectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Scp330.cs b/EXILED/Exiled.API/Features/Items/Scp330.cs
index d0a7e3b9c..689929f12 100644
--- a/EXILED/Exiled.API/Features/Items/Scp330.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp330.cs
@@ -197,7 +197,7 @@ public IEnumerable DropCandy(CandyKindID type, bool dropAll = fals
ipb.Info = new(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp330Pickup pickup = (Scp330Pickup)Pickup.Get(ipb);
+ Scp330Pickup pickup = Pickup.Get(ipb);
if (exposedType is not CandyKindID.None)
pickup.ExposedCandy = exposedType;
@@ -218,7 +218,7 @@ public IEnumerable DropCandy(CandyKindID type, bool dropAll = fals
ipb.Info = new(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp330Pickup pickup = (Scp330Pickup)Pickup.Get(ipb);
+ Scp330Pickup pickup = Pickup.Get(ipb);
if (exposedType is not CandyKindID.None)
pickup.ExposedCandy = exposedType;
diff --git a/EXILED/Exiled.API/Features/Items/Throwable.cs b/EXILED/Exiled.API/Features/Items/Throwable.cs
index 4946d1722..4de522295 100644
--- a/EXILED/Exiled.API/Features/Items/Throwable.cs
+++ b/EXILED/Exiled.API/Features/Items/Throwable.cs
@@ -29,7 +29,7 @@ public Throwable(ThrowableItem itemBase)
{
Base = itemBase;
Base.Projectile.gameObject.SetActive(false);
- Projectile = (Projectile)Pickup.Get(Object.Instantiate(Base.Projectile));
+ Projectile = Pickup.Get(Object.Instantiate(Base.Projectile));
Base.Projectile.gameObject.SetActive(true);
Projectile.Serial = Serial;
}
diff --git a/EXILED/Exiled.API/Features/Lift.cs b/EXILED/Exiled.API/Features/Lift.cs
index 944e5bd77..9a3b183c2 100644
--- a/EXILED/Exiled.API/Features/Lift.cs
+++ b/EXILED/Exiled.API/Features/Lift.cs
@@ -33,7 +33,7 @@ public class Lift : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary ElevatorChamberToLift = new(8);
+ internal static readonly Dictionary ElevatorChamberToLift = new(8, new ComponentsEqualityComparer());
///
/// Internal list that contains all ElevatorDoor for current group.
@@ -76,7 +76,7 @@ internal Lift(ElevatorChamber elevator)
///
/// Gets a value of the internal doors list.
///
- public IReadOnlyCollection Doors => internalDoorsList.Select(x => Door.Get(x).As()).ToList();
+ public IReadOnlyCollection Doors => internalDoorsList.Select(x => Door.Get(x)).ToList();
///
/// Gets a of in the .
@@ -201,7 +201,7 @@ public float AnimationTime
///
/// Gets the .
///
- public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination).As();
+ public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination);
///
/// Gets a of which contains all the instances from the specified .
diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs
index 14f6d8214..3f1f6fc1a 100644
--- a/EXILED/Exiled.API/Features/Map.cs
+++ b/EXILED/Exiled.API/Features/Map.cs
@@ -28,9 +28,6 @@ namespace Exiled.API.Features
using LightContainmentZoneDecontamination;
using MapGeneration;
using MapGeneration.Distributors;
- using Mirror;
- using PlayerRoles;
- using PlayerRoles.PlayableScps.Scp173;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.Ragdolls;
using RelativePositioning;
@@ -39,8 +36,6 @@ namespace Exiled.API.Features
using Utils.Networking;
using Object = UnityEngine.Object;
- using Scp173GameRole = PlayerRoles.PlayableScps.Scp173.Scp173Role;
- using Scp939GameRole = PlayerRoles.PlayableScps.Scp939.Scp939Role;
///
/// A set of tools to easily handle the in-game map.
@@ -57,53 +52,17 @@ public static class Map
///
internal static readonly List TeleportsValue = new(8);
- ///
- /// A list of s on the map.
- ///
- internal static readonly List ToysValue = new();
-
- private static TantrumEnvironmentalHazard tantrumPrefab;
- private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
-
private static AmbientSoundPlayer ambientSoundPlayer;
///
/// Gets the tantrum prefab.
///
- public static TantrumEnvironmentalHazard TantrumPrefab
- {
- get
- {
- if (tantrumPrefab == null)
- {
- Scp173GameRole scp173Role = (Scp173GameRole)RoleTypeId.Scp173.GetRoleBase();
-
- if (scp173Role.SubroutineModule.TryGetSubroutine(out Scp173TantrumAbility scp173TantrumAbility))
- tantrumPrefab = scp173TantrumAbility._tantrumPrefab;
- }
-
- return tantrumPrefab;
- }
- }
+ public static TantrumEnvironmentalHazard TantrumPrefab => TantrumHazard.TantrumPrefab; // TODO: Remove this.
///
/// Gets the amnestic cloud prefab.
///
- public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
- {
- get
- {
- if (amnesticCloudPrefab == null)
- {
- Scp939GameRole scp939Role = (Scp939GameRole)RoleTypeId.Scp939.GetRoleBase();
-
- if (scp939Role.SubroutineModule.TryGetSubroutine(out Scp939AmnesticCloudAbility ability))
- amnesticCloudPrefab = ability._instancePrefab;
- }
-
- return amnesticCloudPrefab;
- }
- }
+ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab => AmnesticCloudHazard.AmnesticCloudPrefab; // TODO: Remove this.
///
/// Gets a value indicating whether decontamination has begun in the light containment zone.
@@ -130,7 +89,7 @@ DecontaminationController.Singleton.NetworkDecontaminationOverride is Decontamin
///
/// Gets all objects.
///
- public static ReadOnlyCollection Toys { get; } = ToysValue.AsReadOnly();
+ public static ReadOnlyCollection Toys => AdminToy.BaseToAdminToy.Values.ToList().AsReadOnly(); // TODO: Obsolete it and make people use AdminToy.List
///
/// Gets or sets the current seed of the map.
@@ -286,21 +245,7 @@ public static void PlayAmbientSound(int id)
/// Whether or not the tantrum will apply the effect.
/// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
/// The instance.
- public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
- {
- TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
-
- if (!isActive)
- tantrum.SynchronizedPosition = new RelativePosition(position);
- else
- tantrum.SynchronizedPosition = new RelativePosition(position + (Vector3.up * 0.25f));
-
- tantrum._destroyed = !isActive;
-
- NetworkServer.Spawn(tantrum.gameObject);
-
- return Hazard.Get(tantrum).Cast();
- }
+ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true) => TantrumHazard.PlaceTantrum(position, isActive); // TODO: Remove this.
///
/// Destroy all objects.
diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs
index 473bb4c14..6824cbab9 100644
--- a/EXILED/Exiled.API/Features/Npc.cs
+++ b/EXILED/Exiled.API/Features/Npc.cs
@@ -17,7 +17,6 @@ namespace Exiled.API.Features
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Components;
-
using Footprinting;
using MEC;
@@ -52,6 +51,20 @@ public Npc(GameObject gameObject)
///
public static new List List => Player.List.OfType().ToList();
+ ///
+ /// Gets or sets the player's position.
+ ///
+ public override Vector3 Position
+ {
+ get => base.Position;
+ set
+ {
+ base.Position = value;
+ if (Role is Roles.FpcRole fpcRole)
+ fpcRole.RelativePosition = new(value);
+ }
+ }
+
///
/// Retrieves the NPC associated with the specified ReferenceHub.
///
diff --git a/EXILED/Exiled.API/Features/Pickups/Pickup.cs b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
index 70db62a8a..6c600dd4c 100644
--- a/EXILED/Exiled.API/Features/Pickups/Pickup.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Pickups
using System.Collections.Generic;
using System.Linq;
+ using Exiled.API.Extensions;
using Exiled.API.Features.Core;
using Exiled.API.Features.Pickups.Projectiles;
using Exiled.API.Interfaces;
@@ -208,6 +209,11 @@ public float PickupTime
///
public ItemType Type => Base.NetworkInfo.ItemId;
+ ///
+ /// Gets the of the item.
+ ///
+ public ItemCategory Category => Type.GetCategory();
+
///
/// Gets or sets a value indicating whether the pickup is locked (can't be picked up).
///
@@ -338,6 +344,15 @@ public static Pickup Get(ItemPickupBase pickupBase)
};
}
+ ///
+ /// Gets an existing or creates a new instance of one.
+ ///
+ /// The to convert into an pickup.
+ /// The specified type.
+ /// The pickup wrapper for the given .
+ public static T Get(ItemPickupBase pickupBase)
+ where T : Pickup => Get(pickupBase) as T;
+
///
/// Gets the given a .
///
@@ -487,6 +502,36 @@ public static IEnumerable Get(IEnumerable gameObjects)
_ => new Pickup(type),
};
+ ///
+ /// Creates and returns a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be cast into a subclass to gain more control over the object.
+ /// - All valid ammo should be cast to the class.
+ /// - All valid firearms (not including the Micro HID) should be cast to the class.
+ /// - All valid keycards should be cast to the class.
+ /// - All valid armor should be cast to the class.
+ /// - All grenades and throwables (not including SCP-018 and SCP-2176) should be cast to the class.
+ ///
+ ///
+ /// The following have their own respective classes:
+ /// - Radios can be cast to .
+ /// - The Micro HID can be cast to .
+ /// - SCP-244 A and B variants can be cast to .
+ /// - SCP-330 can be cast to .
+ /// - SCP-018 can be cast to .
+ /// - SCP-2176 can be cast to .
+ ///
+ ///
+ /// Items that are not listed above do not have a subclass, and can only use the base class.
+ ///
+ ///
+ /// The of the pickup.
+ /// The specified type.
+ /// The created .
+ ///
+ public static Pickup Create(ItemType type)
+ where T : Pickup => Create(type) as T;
+
///
/// Creates and spawns a .
///
@@ -498,6 +543,19 @@ public static IEnumerable Get(IEnumerable gameObjects)
///
public static Pickup CreateAndSpawn(ItemType type, Vector3 position, Quaternion rotation, Player previousOwner = null) => Create(type).Spawn(position, rotation, previousOwner);
+ ///
+ /// Creates and spawns a .
+ ///
+ /// The of the pickup.
+ /// The position to spawn the at.
+ /// The rotation to spawn the .
+ /// An optional previous owner of the item.
+ /// The specified type.
+ /// The . See documentation of for more information on casting.
+ ///
+ public static Pickup CreateAndSpawn(ItemType type, Vector3 position, Quaternion rotation, Player previousOwner = null)
+ where T : Pickup => CreateAndSpawn(type, position, rotation, previousOwner) as T;
+
///
/// Spawns a .
///
diff --git a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
index cd18059bc..5406d6414 100644
--- a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
@@ -83,17 +83,37 @@ internal Projectile(ItemType type)
/// Projectile that are not listed will cause an Exception.
///
///
- /// The of the pickup.
- /// The created .
+ /// The of the projectile.
+ /// The created .
public static Projectile Create(ProjectileType projectiletype) => projectiletype switch
{
ProjectileType.Scp018 => new Scp018Projectile(),
ProjectileType.Flashbang => new FlashbangProjectile(),
ProjectileType.Scp2176 => new Scp2176Projectile(),
ProjectileType.FragGrenade => new ExplosionGrenadeProjectile(ItemType.GrenadeHE),
- _ => throw new System.Exception($"ProjectileType does not contain a valid value : {projectiletype}"),
+ _ => throw new Exception($"ProjectileType does not contain a valid value : {projectiletype}"),
};
+ ///
+ /// Creates and returns a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be casted into a subclass to gain more control over the object.
+ /// The following have their own respective classes:
+ /// - FragGrenade can be casted to .
+ /// - Flashbang can be casted to .
+ /// - Scp018 A and B variants can be casted to .
+ /// - Scp2176 can be casted to .
+ ///
+ ///
+ /// Projectile that are not listed will cause an Exception.
+ ///
+ ///
+ /// The of the projectile.
+ /// The specified type.
+ /// The created .
+ public static Projectile Create(ProjectileType projectiletype)
+ where T : Projectile => Create(projectiletype) as T;
+
///
/// Spawns a .
///
@@ -110,14 +130,27 @@ public static Projectile Spawn(Projectile pickup, Vector3 position, Quaternion r
///
/// Creates and spawns a .
///
- /// The of the pickup.
+ /// The of the projectile.
/// The position to spawn the at.
/// The rotation to spawn the .
/// Whether the should be in active state after spawn.
/// An optional previous owner of the item.
- /// The . See documentation of for more information on casting.
+ /// The . See documentation of for more information on casting.
public static Projectile CreateAndSpawn(ProjectileType type, Vector3 position, Quaternion rotation, bool shouldBeActive = true, Player previousOwner = null) => Create(type).Spawn(position, rotation, shouldBeActive, previousOwner);
+ ///
+ /// Creates and spawns a .
+ ///
+ /// The of the projectile.
+ /// The position to spawn the at.
+ /// The rotation to spawn the .
+ /// Whether the should be in active state after spawn.
+ /// An optional previous owner of the item.
+ /// The specified type.
+ /// The . See documentation of for more information on casting.
+ public static Projectile CreateAndSpawn(ProjectileType type, Vector3 position, Quaternion rotation, bool shouldBeActive = true, Player previousOwner = null)
+ where T : Projectile => CreateAndSpawn(type, position, rotation, shouldBeActive, previousOwner) as T;
+
///
/// Activates the current .
///
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 4991e13d4..0885000f7 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -80,6 +80,16 @@ public class Player : TypeCastObject, IEntity, IWorldSpace
/// A list of the player's items.
///
internal readonly List ItemsValue = new(8);
+
+ ///
+ /// A dictionary of custom item category limits.
+ ///
+ internal Dictionary CustomCategoryLimits = new();
+
+ ///
+ /// A dictionary of custom ammo limits.
+ ///
+ internal Dictionary CustomAmmoLimits = new();
#pragma warning restore SA1401
private readonly HashSet componentsInChildren = new();
@@ -282,6 +292,7 @@ public AuthenticationType AuthenticationType
"northwood" => AuthenticationType.Northwood,
"localhost" => AuthenticationType.LocalHost,
"ID_Dedicated" => AuthenticationType.DedicatedServer,
+ "offline" => AuthenticationType.Offline,
_ => AuthenticationType.Unknown,
};
}
@@ -503,7 +514,7 @@ public Player Cuffer
///
///
///
- public Vector3 Position
+ public virtual Vector3 Position
{
get => Transform.position;
set => ReferenceHub.TryOverridePosition(value, Vector3.zero);
@@ -958,7 +969,7 @@ public Item CurrentItem
///
/// Gets the armor that the player is currently wearing. Value will be if the player is not wearing any armor.
///
- public Armor CurrentArmor => Inventory.TryGetBodyArmor(out BodyArmor armor) ? (Armor)Item.Get(armor) : null;
+ public Armor CurrentArmor => Inventory.TryGetBodyArmor(out BodyArmor armor) ? Item.Get(armor) : null;
///
/// Gets the class.
@@ -1266,8 +1277,13 @@ public static Player Get(GameObject gameObject)
if (Dictionary.TryGetValue(gameObject, out Player player))
return player;
- UnverifiedPlayers.TryGetValue(gameObject, out player);
- return player;
+ if (UnverifiedPlayers.TryGetValue(gameObject, out player))
+ return player;
+
+ if (ReferenceHub.TryGetHub(gameObject, out ReferenceHub hub))
+ return new(hub);
+
+ return null;
}
///
@@ -1295,7 +1311,7 @@ public static Player Get(string args)
if (int.TryParse(args, out int id))
return Get(id);
- if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood"))
+ if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood") || args.EndsWith("@offline"))
{
foreach (Player player in Dictionary.Values)
{
@@ -2319,6 +2335,16 @@ public void Broadcast(ushort duration, string message, global::Broadcast.Broadca
public void AddAmmo(AmmoType ammoType, ushort amount) =>
Inventory.ServerAddAmmo(ammoType.GetItemType(), amount);
+ ///
+ /// Adds the amount of a specified ammo type to player's inventory.
+ ///
+ /// A dictionary of ammo types that will be added.
+ public void AddAmmo(Dictionary ammoBag)
+ {
+ foreach (KeyValuePair kvp in ammoBag)
+ AddAmmo(kvp.Key, kvp.Value);
+ }
+
///
/// Adds the amount of a weapon's ammo type to the player's inventory.
///
@@ -2338,6 +2364,16 @@ public void SetAmmo(AmmoType ammoType, ushort amount)
Inventory.ServerSetAmmo(itemType, amount);
}
+ ///
+ /// Sets the amount of a specified ammo type to player's inventory.
+ ///
+ /// A dictionary of ammo types that will be added.
+ public void SetAmmo(Dictionary ammoBag)
+ {
+ foreach (KeyValuePair kvp in ammoBag)
+ SetAmmo(kvp.Key, kvp.Value);
+ }
+
///
/// Gets the ammo count of a specified ammo type in a player's inventory.
///
@@ -2357,21 +2393,170 @@ public bool DropAmmo(AmmoType ammoType, ushort amount, bool checkMinimals = fals
///
/// Gets the maximum amount of ammo the player can hold, given the ammo .
- /// This method factors in the armor the player is wearing, as well as server configuration.
- /// For the maximum amount of ammo that can be given regardless of worn armor and server configuration, see .
///
/// The of the ammo to check.
- /// The maximum amount of ammo this player can carry. Guaranteed to be between 0 and .
- public int GetAmmoLimit(AmmoType type) =>
- InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
+ /// If the method should ignore the armor the player is wearing.
+ /// The maximum amount of ammo this player can carry.
+ public ushort GetAmmoLimit(AmmoType type, bool ignoreArmor = false)
+ {
+ if (ignoreArmor)
+ {
+ if (CustomAmmoLimits.TryGetValue(type, out ushort limit))
+ return limit;
+
+ ItemType itemType = type.GetItemType();
+ return ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FirstOrDefault(x => x.AmmoType == itemType).Limit;
+ }
+
+ return InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
+ }
+
+ ///
+ /// Gets the maximum amount of ammo the player can hold, given the ammo .
+ /// This limit will scale with the armor the player is wearing.
+ /// For armor ammo limits, see .
+ ///
+ /// The of the ammo to check.
+ /// The number that will define the new limit.
+ public void SetAmmoLimit(AmmoType ammoType, ushort limit)
+ {
+ CustomAmmoLimits[ammoType] = limit;
+
+ ItemType itemType = ammoType.GetItemType();
+ int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType);
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(2ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteAmmoLimit(new() { Limit = limit, AmmoType = itemType, });
+ });
+ }
+
+ ///
+ /// Reset a custom limit.
+ ///
+ /// The of the ammo to reset.
+ public void ResetAmmoLimit(AmmoType ammoType)
+ {
+ if (!HasCustomAmmoLimit(ammoType))
+ {
+ Log.Error($"{nameof(Player)}.{nameof(ResetAmmoLimit)}(AmmoType): AmmoType.{ammoType} does not have a custom limit.");
+ return;
+ }
+
+ CustomAmmoLimits.Remove(ammoType);
+
+ ItemType itemType = ammoType.GetItemType();
+ int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType);
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(2ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteAmmoLimit(ServerConfigSynchronizer.Singleton.AmmoLimitsSync[index]);
+ });
+ }
+
+ ///
+ /// Check if the player has a custom limit for a specific .
+ ///
+ /// The to check.
+ /// If the player has a custom limit for the specific .
+ public bool HasCustomAmmoLimit(AmmoType ammoType) => CustomAmmoLimits.ContainsKey(ammoType);
///
/// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration.
///
/// The to check.
+ /// If the method should ignore the armor the player is wearing.
/// The maximum amount of items in the category that the player can hold.
- public int GetCategoryLimit(ItemCategory category) =>
- InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
+ public sbyte GetCategoryLimit(ItemCategory category, bool ignoreArmor = false)
+ {
+ int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);
+
+ if (ignoreArmor && index != -1)
+ {
+ if (CustomCategoryLimits.TryGetValue(category, out sbyte customLimit))
+ return customLimit;
+
+ return ServerConfigSynchronizer.Singleton.CategoryLimits[index];
+ }
+
+ sbyte limit = InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
+
+ return limit == -1 ? (sbyte)1 : limit;
+ }
+
+ ///
+ /// Set the maximum amount of an the player can hold. Only works with , , , and .
+ /// This limit will scale with the armor the player is wearing.
+ /// For armor category limits, see .
+ ///
+ /// The to check.
+ /// The number that will define the new limit.
+ public void SetCategoryLimit(ItemCategory category, sbyte limit)
+ {
+ int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);
+
+ if (index == -1)
+ {
+ Log.Error($"{nameof(Player)}.{nameof(SetCategoryLimit)}(ItemCategory, sbyte): Cannot set category limit for ItemCategory.{category}.");
+ return;
+ }
+
+ CustomCategoryLimits[category] = limit;
+
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(1ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteSByte(limit);
+ });
+ }
+
+ ///
+ /// Reset a custom limit. Only works with , , , and .
+ ///
+ /// The of the category to reset.
+ public void ResetCategoryLimit(ItemCategory category)
+ {
+ int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);
+
+ if (index == -1)
+ {
+ Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory, sbyte): Cannot reset category limit for ItemCategory.{category}.");
+ return;
+ }
+
+ if (!HasCustomCategoryLimit(category))
+ {
+ Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory): ItemCategory.{category} does not have a custom limit.");
+ return;
+ }
+
+ CustomCategoryLimits.Remove(category);
+
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(1ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteSByte(ServerConfigSynchronizer.Singleton.CategoryLimits[index]);
+ });
+ }
+
+ ///
+ /// Check if the player has a custom limit for a specific .
+ ///
+ /// The to check.
+ /// If the player has a custom limit for the specific .
+ public bool HasCustomCategoryLimit(ItemCategory category) => CustomCategoryLimits.ContainsKey(category);
///
/// Adds an item of the specified type with default durability(ammo/charge) and no mods to the player's inventory.
@@ -2582,7 +2767,7 @@ public void AddItem(Firearm item, IEnumerable identifiers)
/// The that was added.
public Item AddItem(FirearmPickup pickup, IEnumerable identifiers)
{
- Firearm firearm = (Firearm)Item.Get(Inventory.ServerAddItem(pickup.Type, pickup.Serial, pickup.Base));
+ Firearm firearm = Item.Get(Inventory.ServerAddItem(pickup.Type, pickup.Serial, pickup.Base));
if (identifiers is not null)
firearm.AddAttachment(identifiers);
@@ -2728,8 +2913,8 @@ public void ResetInventory(IEnumerable newItems)
///
public void ClearInventory(bool destroy = true)
{
- ClearItems(destroy);
ClearAmmo();
+ ClearItems(destroy);
}
///
@@ -3221,7 +3406,7 @@ public void ChangeEffectIntensity(string effectName, byte intensity, float durat
/// Whether or not the tantrum will apply the effect.
/// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
/// The instance..
- public TantrumHazard PlaceTantrum(bool isActive = true) => Map.PlaceTantrum(Position, isActive);
+ public TantrumHazard PlaceTantrum(bool isActive = true) => TantrumHazard.PlaceTantrum(Position, isActive);
///
/// Gives a new to the player.
diff --git a/EXILED/Exiled.API/Features/PrefabHelper.cs b/EXILED/Exiled.API/Features/PrefabHelper.cs
index de97e15d5..fd18e9fe9 100644
--- a/EXILED/Exiled.API/Features/PrefabHelper.cs
+++ b/EXILED/Exiled.API/Features/PrefabHelper.cs
@@ -41,6 +41,21 @@ public static PrefabAttribute GetPrefabAttribute(this PrefabType prefabType)
return type.GetField(Enum.GetName(type, prefabType)).GetCustomAttribute();
}
+ ///
+ /// Gets the prefab of the specified .
+ ///
+ /// The to get prefab of.
+ /// The to get.
+ /// Returns the prefab component as {T}.
+ public static T GetPrefab(PrefabType type)
+ where T : Component
+ {
+ if (!Stored.TryGetValue(type, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
+ return null;
+
+ return component;
+ }
+
///
/// Spawns a prefab on server.
///
@@ -68,9 +83,7 @@ public static GameObject Spawn(PrefabType prefabType, Vector3 position = default
public static T Spawn(PrefabType prefabType, Vector3 position = default, Quaternion rotation = default)
where T : Component
{
- if (!Stored.TryGetValue(prefabType, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
- return null;
- T obj = UnityEngine.Object.Instantiate(component, position, rotation);
+ T obj = UnityEngine.Object.Instantiate(GetPrefab(prefabType), position, rotation);
NetworkServer.Spawn(obj.gameObject);
return obj;
}
diff --git a/EXILED/Exiled.API/Features/Ragdoll.cs b/EXILED/Exiled.API/Features/Ragdoll.cs
index 9d73104fe..e1aefa56a 100644
--- a/EXILED/Exiled.API/Features/Ragdoll.cs
+++ b/EXILED/Exiled.API/Features/Ragdoll.cs
@@ -40,7 +40,7 @@ public class Ragdoll : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary BasicRagdollToRagdoll = new(250);
+ internal static readonly Dictionary BasicRagdollToRagdoll = new(250, new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Recontainer.cs b/EXILED/Exiled.API/Features/Recontainer.cs
index bdbbfcdf7..a63f8ada5 100644
--- a/EXILED/Exiled.API/Features/Recontainer.cs
+++ b/EXILED/Exiled.API/Features/Recontainer.cs
@@ -35,6 +35,11 @@ public static class Recontainer
///
public static bool IsCassieBusy => Base.CassieBusy;
+ ///
+ /// Gets a value about how many generator have been activated.
+ ///
+ public static int EngagedGeneratorCount => Base._prevEngaged;
+
///
/// Gets or sets a value indicating whether the containment zone is open.
///
diff --git a/EXILED/Exiled.API/Features/Roles/FpcRole.cs b/EXILED/Exiled.API/Features/Roles/FpcRole.cs
index 27abeded8..4ca71d8c8 100644
--- a/EXILED/Exiled.API/Features/Roles/FpcRole.cs
+++ b/EXILED/Exiled.API/Features/Roles/FpcRole.cs
@@ -8,9 +8,11 @@
namespace Exiled.API.Features.Roles
{
using System.Collections.Generic;
+ using System.Reflection;
using Exiled.API.Features.Pools;
+ using HarmonyLib;
using PlayerRoles;
using PlayerRoles.FirstPersonControl;
@@ -24,6 +26,7 @@ namespace Exiled.API.Features.Roles
///
public abstract class FpcRole : Role
{
+ private static FieldInfo enableFallDamageField;
private bool isUsingStamina = true;
///
@@ -47,14 +50,36 @@ protected FpcRole(FpcStandardRoleBase baseRole)
public FpcStandardRoleBase FirstPersonController { get; }
///
- /// Gets or sets the player's relative position.
+ /// Gets or sets the player's relative position as perceived by the server.
///
public RelativePosition RelativePosition
+ {
+ get => new(Owner.Position);
+ set => Owner.Position = value.Position;
+ }
+
+ ///
+ /// Gets or sets the player's relative position as perceived by the client.
+ ///
+ public RelativePosition ClientRelativePosition
{
get => FirstPersonController.FpcModule.Motor.ReceivedPosition;
set => FirstPersonController.FpcModule.Motor.ReceivedPosition = value;
}
+ ///
+ /// Gets or sets a value indicating whether if the player should get damage.
+ ///
+ public bool IsFallDamageEnable
+ {
+ get => FirstPersonController.FpcModule.Motor._enableFallDamage;
+ set
+ {
+ enableFallDamageField ??= AccessTools.Field(typeof(FpcMotor), nameof(FpcMotor._enableFallDamage));
+ enableFallDamageField.SetValue(FirstPersonController.FpcModule.Motor, value);
+ }
+ }
+
///
/// Gets or sets a value indicating whether if a rotation is detected on the player.
///
diff --git a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs
index aecc872d4..eff90bc44 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs
@@ -25,7 +25,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-049.
///
- public class Scp049Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp049Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs
index 8782137a7..a3de5059c 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs
@@ -16,6 +16,7 @@ namespace Exiled.API.Features.Roles
using MapGeneration;
using Mirror;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.Scp079;
using PlayerRoles.PlayableScps.Scp079.Cameras;
using PlayerRoles.PlayableScps.Scp079.Pinging;
@@ -31,7 +32,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-079.
///
- public class Scp079Role : Role, ISubroutinedScpRole
+ public class Scp079Role : Role, ISubroutinedScpRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp096Role.cs b/EXILED/Exiled.API/Features/Roles/Scp096Role.cs
index 6d2662ad3..f30342b17 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp096Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp096Role.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Roles
using System.Linq;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp096;
using PlayerRoles.Subroutines;
@@ -20,7 +21,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-096.
///
- public class Scp096Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp096Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp106Role.cs b/EXILED/Exiled.API/Features/Roles/Scp106Role.cs
index 826e756a5..9480a37bc 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp106Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp106Role.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Roles
using Exiled.API.Enums;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp049;
using PlayerRoles.PlayableScps.Scp106;
@@ -24,7 +25,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-106.
///
- public class Scp106Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp106Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp173Role.cs b/EXILED/Exiled.API/Features/Roles/Scp173Role.cs
index 23536f696..3a5f625ca 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp173Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp173Role.cs
@@ -13,6 +13,7 @@ namespace Exiled.API.Features.Roles
using Exiled.API.Features.Hazards;
using Mirror;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp173;
using PlayerRoles.Subroutines;
@@ -23,7 +24,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-173.
///
- public class Scp173Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp173Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs b/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs
index 3f7f6fae1..0d23e48ba 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs
@@ -23,7 +23,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-3114.
///
- public class Scp3114Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp3114Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs
index b6dca72b8..ec404f349 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs
@@ -13,6 +13,7 @@ namespace Exiled.API.Features.Roles
using Exiled.API.Features.Pools;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.PlayableScps.Scp939.Mimicry;
@@ -28,7 +29,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-939.
///
- public class Scp939Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp939Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs
index 8d0b70fe3..4ca8bc4d3 100644
--- a/EXILED/Exiled.API/Features/Room.cs
+++ b/EXILED/Exiled.API/Features/Room.cs
@@ -32,7 +32,7 @@ public class Room : MonoBehaviour, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary RoomIdentifierToRoom = new(250);
+ internal static readonly Dictionary RoomIdentifierToRoom = new(250, new ComponentsEqualityComparer());
///
/// Gets a of which contains all the instances.
diff --git a/EXILED/Exiled.API/Features/Server.cs b/EXILED/Exiled.API/Features/Server.cs
index 26cf64c98..b1b05e8c1 100644
--- a/EXILED/Exiled.API/Features/Server.cs
+++ b/EXILED/Exiled.API/Features/Server.cs
@@ -111,6 +111,15 @@ public static string Name
///
public static double Tps => Math.Round(1f / Time.smoothDeltaTime);
+ ///
+ /// Gets or sets the max ticks per second of the server.
+ ///
+ public static short MaxTps
+ {
+ get => ServerStatic.ServerTickrate;
+ set => ServerStatic.ServerTickrate = value;
+ }
+
///
/// Gets the actual frametime of the server.
///
@@ -178,6 +187,11 @@ public static bool IsWhitelisted
set => ServerConsole.WhiteListEnabled = value;
}
+ ///
+ /// Gets the list of user IDs of players currently whitelisted.
+ ///
+ public static HashSet WhitelistedPlayers => WhiteList.Users;
+
///
/// Gets a value indicating whether or not this server is verified.
///
diff --git a/EXILED/Exiled.API/Features/TeslaGate.cs b/EXILED/Exiled.API/Features/TeslaGate.cs
index 7ebdf6480..4d7909592 100644
--- a/EXILED/Exiled.API/Features/TeslaGate.cs
+++ b/EXILED/Exiled.API/Features/TeslaGate.cs
@@ -27,7 +27,7 @@ public class TeslaGate : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary BaseTeslaGateToTeslaGate = new(10);
+ internal static readonly Dictionary BaseTeslaGateToTeslaGate = new(10, new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -178,7 +178,7 @@ public bool UseInstantBurst
///
/// Gets a of which contains all the tantrums to destroy.
///
- public IEnumerable TantrumsToDestroy => Base.TantrumsToBeDestroyed.Select(x => Hazard.Get(x) as TantrumHazard);
+ public IEnumerable TantrumsToDestroy => Base.TantrumsToBeDestroyed.Select(x => Hazard.Get(x));
///
/// Gets a of which contains all the players inside the hurt range.
diff --git a/EXILED/Exiled.API/Features/Toys/AdminToy.cs b/EXILED/Exiled.API/Features/Toys/AdminToy.cs
index fdbe03da1..5287096a1 100644
--- a/EXILED/Exiled.API/Features/Toys/AdminToy.cs
+++ b/EXILED/Exiled.API/Features/Toys/AdminToy.cs
@@ -7,6 +7,7 @@
namespace Exiled.API.Features.Toys
{
+ using System.Collections.Generic;
using System.Linq;
using AdminToys;
@@ -14,6 +15,7 @@ namespace Exiled.API.Features.Toys
using Enums;
using Exiled.API.Interfaces;
using Footprinting;
+ using InventorySystem.Items;
using Mirror;
using UnityEngine;
@@ -23,6 +25,11 @@ namespace Exiled.API.Features.Toys
///
public abstract class AdminToy : IWorldSpace
{
+ ///
+ /// A dictionary of all 's that have been converted into .
+ ///
+ internal static readonly Dictionary BaseToAdminToy = new(new ComponentsEqualityComparer());
+
///
/// Initializes a new instance of the class.
///
@@ -33,9 +40,14 @@ internal AdminToy(AdminToyBase toyAdminToyBase, AdminToyType type)
AdminToyBase = toyAdminToyBase;
ToyType = type;
- Map.ToysValue.Add(this);
+ BaseToAdminToy.Add(toyAdminToyBase, this);
}
+ ///
+ /// Gets a list of all 's on the server.
+ ///
+ public static IReadOnlyCollection List => BaseToAdminToy.Values;
+
///
/// Gets the original .
///
@@ -130,7 +142,31 @@ public bool IsStatic
///
/// The instance.
/// The corresponding instance.
- public static AdminToy Get(AdminToyBase adminToyBase) => Map.Toys.FirstOrDefault(x => x.AdminToyBase == adminToyBase);
+ public static AdminToy Get(AdminToyBase adminToyBase)
+ {
+ if (adminToyBase == null)
+ return null;
+
+ if (BaseToAdminToy.TryGetValue(adminToyBase, out AdminToy adminToy))
+ return adminToy;
+
+ return adminToyBase switch
+ {
+ LightSourceToy lightSourceToy => new Light(lightSourceToy),
+ PrimitiveObjectToy primitiveObjectToy => new Primitive(primitiveObjectToy),
+ ShootingTarget shootingTarget => new ShootingTargetToy(shootingTarget),
+ _ => throw new System.NotImplementedException()
+ };
+ }
+
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an admintoy.
+ /// The specified type.
+ /// The admintoy wrapper for the given .
+ public static T Get(AdminToyBase adminToyBase)
+ where T : AdminToy => Get(adminToyBase) as T;
///
/// Spawns the toy into the game. Use to remove it.
@@ -147,7 +183,7 @@ public bool IsStatic
///
public void Destroy()
{
- Map.ToysValue.Remove(this);
+ BaseToAdminToy.Remove(AdminToyBase);
NetworkServer.Destroy(AdminToyBase.gameObject);
}
}
diff --git a/EXILED/Exiled.API/Features/Window.cs b/EXILED/Exiled.API/Features/Window.cs
index 961fbab88..63ca362fd 100644
--- a/EXILED/Exiled.API/Features/Window.cs
+++ b/EXILED/Exiled.API/Features/Window.cs
@@ -25,7 +25,7 @@ public class Window : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary BreakableWindowToWindow = new();
+ internal static readonly Dictionary BreakableWindowToWindow = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
index d052b914c..e31fe2615 100644
--- a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
+++ b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -17,7 +17,7 @@ namespace Exiled.CreditTags.Features
public static class DatabaseHandler
{
- private const string Url = "https://raw.githubusercontent.com/Exiled-Official/CreditTags/main/data.yml";
+ private const string Url = "https://raw.githubusercontent.com/ExMod-Team/CreditTags/main/data.yml";
private const string ETagCacheFileName = "etag_cache.txt";
private const string DatabaseCacheFileName = "data.yml";
private const int CacheTimeInMinutes = 5;
diff --git a/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs b/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
index 8f35712fb..1620477ba 100644
--- a/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
+++ b/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
@@ -8,6 +8,7 @@
namespace Exiled.CustomItems.Patches
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -52,7 +53,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, item.LocalIndex),
new(OpCodes.Brfalse_S, continueLabel),
diff --git a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
index 51e4ac008..61f88ec2c 100644
--- a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
+++ b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
@@ -537,6 +537,16 @@ public virtual void AddRole(Player player)
Log.Debug($"{Name}: Adding {itemName} to inventory.");
TryAddItem(player, itemName);
}
+
+ if (Ammo.Count > 0)
+ {
+ Log.Debug($"{Name}: Adding Ammo to {player.Nickname} inventory.");
+ foreach (AmmoType type in EnumUtils.Values)
+ {
+ if (type != AmmoType.None)
+ player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), player.ReferenceHub) : Ammo[type] : (ushort)0);
+ }
+ }
});
Log.Debug($"{Name}: Setting health values.");
@@ -910,25 +920,6 @@ private void OnInternalChangingRole(ChangingRoleEventArgs ev)
{
RemoveRole(ev.Player);
}
- else if (Check(ev.Player))
- {
- Log.Debug($"{Name}: Checking ammo stuff {Ammo.Count}");
- if (Ammo.Count > 0)
- {
- Log.Debug($"{Name}: Clearing ammo");
- ev.Ammo.Clear();
- Timing.CallDelayed(
- 0.5f,
- () =>
- {
- foreach (AmmoType type in Enum.GetValues(typeof(AmmoType)))
- {
- if (type != AmmoType.None)
- ev.Player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), ev.Player.ReferenceHub) : Ammo[type] : (ushort)0);
- }
- });
- }
- }
}
private void OnSpawningRagdoll(SpawningRagdollEventArgs ev)
diff --git a/EXILED/Exiled.Events/Commands/TpsCommand.cs b/EXILED/Exiled.Events/Commands/TpsCommand.cs
new file mode 100644
index 000000000..2d7412919
--- /dev/null
+++ b/EXILED/Exiled.Events/Commands/TpsCommand.cs
@@ -0,0 +1,46 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Commands
+{
+ using System;
+
+ using CommandSystem;
+ using Exiled.API.Features;
+
+ ///
+ /// Command for showing current server TPS.
+ ///
+ [CommandHandler(typeof(RemoteAdminCommandHandler))]
+ [CommandHandler(typeof(GameConsoleCommandHandler))]
+ public class TpsCommand : ICommand
+ {
+ ///
+ public string Command { get; } = "tps";
+
+ ///
+ public string[] Aliases { get; } = Array.Empty();
+
+ ///
+ public string Description { get; } = "Shows the current TPS of the server";
+
+ ///
+ public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
+ {
+ double diff = Server.Tps / Server.MaxTps;
+ string color = diff switch
+ {
+ > 0.9 => "green",
+ > 0.5 => "yellow",
+ _ => "red"
+ };
+
+ response = $"{Server.Tps}/{Server.MaxTps}";
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs
index 54f6a3f74..d10d34631 100644
--- a/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs
+++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs
@@ -10,7 +10,7 @@ namespace Exiled.Events.EventArgs.Interfaces
using Exiled.API.Features.Hazards;
///
- /// Event args for all related events.
+ /// Event args for all related events.
///
public interface IHazardEvent : IExiledEvent
{
diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.cs
new file mode 100644
index 000000000..45a3072e7
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.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.Items;
+
+ ///
+ /// Event args used for all related events.
+ ///
+ public interface IScp330Event : IItemEvent
+ {
+ ///
+ /// Gets the triggering the event.
+ ///
+ public Scp330 Scp330 { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs
index 6c34c16e8..67b28f5b6 100644
--- a/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs
+++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs
@@ -10,12 +10,12 @@ namespace Exiled.Events.EventArgs.Interfaces
using Exiled.API.Features.Items;
///
- /// Event args used for all related events.
+ /// Event args used for all related events.
///
public interface IUsableEvent : IItemEvent
{
///
- /// Gets the triggering the event.
+ /// Gets the triggering the event.
///
public Usable Usable { get; }
}
diff --git a/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
index 0e1c8fbed..fad3ec0b5 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
@@ -38,17 +38,13 @@ public class ChangingAttachmentsEventArgs : IPlayerEvent, IDeniableEvent, IFirea
///
///
///
- public ChangingAttachmentsEventArgs(
- Player player,
- Firearm firearm,
- uint code,
- bool isAllowed = true)
+ public ChangingAttachmentsEventArgs(Player player, InventorySystem.Items.Firearms.Firearm firearm, uint code, bool isAllowed = true)
{
Player = player;
- Firearm = firearm;
- CurrentAttachmentIdentifiers = firearm.AttachmentIdentifiers;
- NewAttachmentIdentifiers = firearm.FirearmType.GetAttachmentIdentifiers(code).ToList();
- CurrentCode = firearm.Base.GetCurrentAttachmentsCode();
+ Firearm = Item.Get(firearm);
+ CurrentAttachmentIdentifiers = Firearm.AttachmentIdentifiers;
+ NewAttachmentIdentifiers = Firearm.FirearmType.GetAttachmentIdentifiers(code).ToList();
+ CurrentCode = firearm.GetCurrentAttachmentsCode();
NewCode = code;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs
index 1b17a1bb0..6ac613800 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs
@@ -7,6 +7,8 @@
namespace Exiled.Events.EventArgs.Item
{
+ using System;
+
using Exiled.API.Features;
using Exiled.API.Features.Items;
using Exiled.Events.EventArgs.Interfaces;
@@ -25,8 +27,10 @@ public class ChargingJailbirdEventArgs : IPlayerEvent, IItemEvent, IDeniableEven
public ChargingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swingItem, bool isAllowed = true)
{
Player = Player.Get(player);
- Item = Item.Get(swingItem);
+ Jailbird = (Jailbird)Item.Get(swingItem);
+#pragma warning disable CS0618
IsAllowed = isAllowed;
+#pragma warning restore CS0618
}
///
@@ -35,13 +39,23 @@ public ChargingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.Item
public Player Player { get; }
///
- /// Gets the that is being charged. This will always be a .
+ /// Gets the that is being charged.
+ ///
+ public Jailbird Jailbird { get; }
+
+ ///
+ /// Gets the that is being charged.
///
- public Item Item { get; }
+ public Item Item => Jailbird;
///
/// Gets or sets a value indicating whether or not the Jailbird can be charged.
///
- public bool IsAllowed { get; set; }
+ public bool IsAllowed
+ {
+ get;
+ [Obsolete("This event cannot be denied as it will cause desync.")]
+ set;
+ }
}
}
diff --git a/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs
index 17c0319b0..6c8f0fce8 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs
@@ -25,7 +25,7 @@ public class SwingingEventArgs : IPlayerEvent, IItemEvent, IDeniableEvent
public SwingingEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swingItem, bool isAllowed = true)
{
Player = Player.Get(player);
- Item = Item.Get(swingItem);
+ Jailbird = (Jailbird)Item.Get(swingItem);
IsAllowed = isAllowed;
}
@@ -34,10 +34,15 @@ public SwingingEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swi
///
public Player Player { get; }
+ ///
+ /// Gets the that is being swung.
+ ///
+ public Jailbird Jailbird { get; }
+
///
/// Gets the that is being swung.
///
- public Item Item { get; }
+ public Item Item => Jailbird;
///
/// Gets or sets a value indicating whether or not the item can be swung.
diff --git a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
index 28b7b30f0..a0dbd7920 100644
--- a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
@@ -37,7 +37,7 @@ public class ExplodingGrenadeEventArgs : IPlayerEvent, IDeniableEvent
public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionGrenade grenade, Collider[] targets)
{
Player = Player.Get(thrower.Hub);
- Projectile = (EffectGrenadeProjectile)Pickup.Get(grenade);
+ Projectile = Pickup.Get(grenade);
Position = position;
TargetsToAffect = ListPool.Pool.Get();
@@ -97,7 +97,7 @@ public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionG
public ExplodingGrenadeEventArgs(Player thrower, EffectGrenade grenade, List targetsToAffect, bool isAllowed = true)
{
Player = thrower ?? Server.Host;
- Projectile = (EffectGrenadeProjectile)Pickup.Get(grenade);
+ Projectile = Pickup.Get(grenade);
Position = Projectile.Position;
TargetsToAffect = ListPool.Pool.Get(targetsToAffect ?? new());
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
index 1ed101269..fd6a6fa76 100644
--- a/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -10,6 +10,8 @@ namespace Exiled.Events.EventArgs.Map
using API.Features;
using Exiled.API.Features.Pickups;
using Interfaces;
+ using InventorySystem.Items.Usables.Scp244;
+ using MapGeneration;
///
/// Contains all information up to spawning Scp244.
@@ -25,11 +27,10 @@ public class Scp244SpawningEventArgs : IRoomEvent, IPickupEvent, IDeniableEvent
///
///
///
- public Scp244SpawningEventArgs(Room room, Pickup scp244Pickup)
+ public Scp244SpawningEventArgs(RoomIdentifier room, Scp244DeployablePickup scp244Pickup)
{
- Room = room;
- Pickup = scp244Pickup;
- Scp244Pickup = scp244Pickup.As();
+ Room = Room.Get(room);
+ Scp244Pickup = Pickup.Get(scp244Pickup);
}
///
@@ -38,7 +39,7 @@ public Scp244SpawningEventArgs(Room room, Pickup scp244Pickup)
public Room Room { get; }
///
- public Pickup Pickup { get; }
+ public Pickup Pickup => Scp244Pickup;
///
/// Gets a value indicating the pickup being spawning.
diff --git a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
index ac07393e7..f3f623f40 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
@@ -27,7 +27,7 @@ public class CancellingItemUseEventArgs : IPlayerEvent, IDeniableEvent, IUsableE
public CancellingItemUseEventArgs(Player player, UsableItem item)
{
Player = player;
- Usable = Item.Get(item) is Usable usable ? usable : null;
+ Usable = Item.Get(item);
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs
index 403f5f705..2265309e4 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs
@@ -46,7 +46,7 @@ public Item Item
get => newItem;
set
{
- if (!Player.Inventory.UserInventory.Items.TryGetValue(value.Serial, out _))
+ if (value != null && !Player.Inventory.UserInventory.Items.TryGetValue(value.Serial, out _))
throw new InvalidOperationException("ev.NewItem cannot be set to an item they do not have.");
newItem = value;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
index 4cb941b0a..2b9bacdbf 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
@@ -40,7 +40,7 @@ public class ChangingMicroHIDStateEventArgs : IPlayerEvent, IDeniableEvent
public ChangingMicroHIDStateEventArgs(Player player, MicroHIDItem microHID, HidState oldState, HidState newState, bool isAllowed = true)
{
Player = player;
- MicroHID = (MicroHid)Item.Get(microHID);
+ MicroHID = Item.Get(microHID);
OldState = oldState;
NewState = newState;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
index 7b59f2fb9..70af9b581 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
@@ -44,7 +44,7 @@ public class ChangingRadioPresetEventArgs : IPlayerEvent, IItemEvent, IDeniableE
public ChangingRadioPresetEventArgs(Player player, RadioItem item, RadioRangeLevel oldValue, RadioRangeLevel newValue, bool isAllowed = true)
{
Player = player;
- Radio = (Radio)Item.Get(item);
+ Radio = Item.Get(item);
OldValue = (RadioRange)oldValue;
NewValue = (RadioRange)newValue;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
index 552d70a73..c8ab4d9ba 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
@@ -11,11 +11,10 @@ namespace Exiled.Events.EventArgs.Player
using API.Enums;
using API.Features;
+ using Exiled.API.Extensions;
using Exiled.API.Features.Pools;
using Interfaces;
-
- using InventorySystem.Configs;
-
+ using InventorySystem;
using PlayerRoles;
///
@@ -70,17 +69,16 @@ public RoleTypeId NewRole
get => newRole;
set
{
- if (StartingInventories.DefinedInventories.ContainsKey(value))
- {
- Items.Clear();
- Ammo.Clear();
+ InventoryRoleInfo inventory = value.GetInventory();
+
+ Items.Clear();
+ Ammo.Clear();
- foreach (ItemType itemType in StartingInventories.DefinedInventories[value].Items)
- Items.Add(itemType);
+ foreach (ItemType itemType in inventory.Items)
+ Items.Add(itemType);
- foreach (KeyValuePair ammoPair in StartingInventories.DefinedInventories[value].Ammo)
- Ammo.Add(ammoPair.Key, ammoPair.Value);
- }
+ foreach (KeyValuePair ammoPair in inventory.Ammo)
+ Ammo.Add(ammoPair.Key, ammoPair.Value);
newRole = value;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
index 14d298c84..0109a4bba 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
@@ -10,6 +10,7 @@ namespace Exiled.Events.EventArgs.Player
using API.Features;
using Exiled.API.Features.Pickups;
using Interfaces;
+ using InventorySystem.Items.Pickups;
///
/// Contains all information after a player drops an item.
@@ -28,10 +29,10 @@ public class DroppedItemEventArgs : IPlayerEvent, IPickupEvent
///
///
///
- public DroppedItemEventArgs(Player player, Pickup pickup, bool wasThrown)
+ public DroppedItemEventArgs(Player player, ItemPickupBase pickup, bool wasThrown)
{
Player = player;
- Pickup = pickup;
+ Pickup = Pickup.Get(pickup);
WasThrown = wasThrown;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
index 316be5039..97a443ea7 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
@@ -5,7 +5,7 @@
//
// -----------------------------------------------------------------------
-namespace Exiled.Events.EventArgs.Player
+namespace Exiled.Events.EventArgs.Player // TODO: Wrong namespace should be Server
{
using API.Features;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
new file mode 100644
index 000000000..ee152fb34
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
@@ -0,0 +1,47 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Player
+{
+ using API.Enums;
+ using API.Features;
+ using Exiled.Events.EventArgs.Interfaces;
+
+ ///
+ /// Contains all information after freeing a handcuffed player.
+ ///
+ public class RemovedHandcuffsEventArgs : IPlayerEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The cuffer player.
+ /// The target player was uncuffed.
+ /// The reason for removing the handcuffs.
+ public RemovedHandcuffsEventArgs(Player cuffer, Player target, UncuffReason uncuffReason)
+ {
+ Player = cuffer;
+ Target = target;
+ UncuffReason = uncuffReason;
+ }
+
+ ///
+ /// Gets the target player to be cuffed.
+ ///
+ public Player Target { get; }
+
+ ///
+ /// Gets the cuffer player.
+ ///
+ public Player Player { get; }
+
+ ///
+ /// Gets the reason for removing handcuffs.
+ ///
+ public UncuffReason UncuffReason { get; }
+ }
+}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
index c6a90a835..c375bb4b7 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
@@ -7,6 +7,7 @@
namespace Exiled.Events.EventArgs.Player
{
+ using API.Enums;
using API.Features;
using Exiled.Events.EventArgs.Interfaces;
@@ -20,11 +21,13 @@ public class RemovingHandcuffsEventArgs : IPlayerEvent, IDeniableEvent
///
/// The cuffer player.
/// The target player to be uncuffed.
+ /// The reason of removing handcuffs.
/// Indicates whether the event can be executed or not.
- public RemovingHandcuffsEventArgs(Player cuffer, Player target, bool isAllowed = true)
+ public RemovingHandcuffsEventArgs(Player cuffer, Player target, UncuffReason uncuffReason, bool isAllowed = true)
{
Player = cuffer;
Target = target;
+ UncuffReason = uncuffReason;
IsAllowed = isAllowed;
}
@@ -34,13 +37,19 @@ public RemovingHandcuffsEventArgs(Player cuffer, Player target, bool isAllowed =
public Player Target { get; }
///
- /// Gets or sets a value indicating whether or not the player can be handcuffed.
+ /// Gets or sets a value indicating whether or not the player can be handcuffed. Denying the event will only have an effect when is until next major update.
///
+ /// TODO: Update docs and patches
public bool IsAllowed { get; set; }
///
/// Gets the cuffer player.
///
public Player Player { get; }
+
+ ///
+ /// Gets the reason of removing handcuffs.
+ ///
+ public UncuffReason UncuffReason { get; }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
index 273763fda..089695333 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
@@ -28,7 +28,7 @@ public class ThrowingRequestEventArgs : IPlayerEvent, IItemEvent
public ThrowingRequestEventArgs(Player player, ThrowableItem item, ThrowableNetworkHandler.RequestType request)
{
Player = player;
- Throwable = (Throwable)Item.Get(item);
+ Throwable = Item.Get(item);
RequestType = (ThrowRequest)request;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
index aaf654d42..26984b839 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
@@ -29,8 +29,8 @@ public class ThrownProjectileEventArgs : IPlayerEvent, IItemEvent, IPickupEvent
public ThrownProjectileEventArgs(ThrownProjectile projectile, Player player, ThrowableItem item)
{
Player = player;
- Throwable = (Throwable)Item.Get(item);
- Projectile = (Projectile)Pickup.Get(projectile);
+ Throwable = Item.Get(item);
+ Projectile = Pickup.Get(projectile);
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
index b8990f85d..3a87a66a1 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
@@ -35,7 +35,7 @@ public class TogglingFlashlightEventArgs : IPlayerEvent, IDeniableEvent, IItemEv
public TogglingFlashlightEventArgs(ReferenceHub hub, ToggleableLightItemBase flashlight, bool newState)
{
Player = Player.Get(hub);
- Flashlight = (Flashlight)Item.Get(flashlight);
+ Flashlight = Item.Get(flashlight);
initialState = newState;
NewState = newState;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
index 67db54505..38d0fe09b 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
@@ -36,7 +36,7 @@ public class TogglingRadioEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent
public TogglingRadioEventArgs(Player player, RadioItem radio, bool newState, bool isAllowed = true)
{
Player = player;
- Radio = (Radio)Item.Get(radio);
+ Radio = Item.Get(radio);
NewState = newState;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
index d98f014ed..402d20e1b 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
@@ -40,7 +40,7 @@ public class UsingMicroHIDEnergyEventArgs : IPlayerEvent, IDeniableEvent, IItemE
public UsingMicroHIDEnergyEventArgs(Player player, MicroHIDItem microHIDitem, HidState currentState, float drain, bool isAllowed = true)
{
Player = player;
- MicroHID = (MicroHid)Item.Get(microHIDitem);
+ MicroHID = Item.Get(microHIDitem);
CurrentState = currentState;
Drain = drain;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
index 90369883f..1f07dadde 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
@@ -36,7 +36,7 @@ public class UsingRadioBatteryEventArgs : IPlayerEvent, IDeniableEvent, IItemEve
///
public UsingRadioBatteryEventArgs(RadioItem radio, Player player, float drain, bool isAllowed = true)
{
- Radio = (Radio)Item.Get(radio);
+ Radio = Item.Get(radio);
Player = player;
Drain = drain;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
index e2c2c0728..e897dbb04 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
@@ -38,7 +38,7 @@ public FinishingRecallEventArgs(Player target, Player scp049, BasicRagdoll ragdo
Scp049 = Player.Role.As();
Target = target;
Ragdoll = Ragdoll.Get(ragdoll);
- IsAllowed = isAllowed && Target.Role is SpectatorRole spectatorRole && spectatorRole.IsReadyToRespawn;
+ IsAllowed = isAllowed;
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs
index 84a2e93ad..49b5dbebd 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs
@@ -22,10 +22,16 @@ public class RecontainedEventArgs : IScp079Event
///
///
///
- public RecontainedEventArgs(Player player)
+ ///
+ ///
+ ///
+ public RecontainedEventArgs(Player player, PlayerRoles.PlayableScps.Scp079.Scp079Recontainer scp079Recontainer)
{
Player = player;
Scp079 = player.Role.As();
+ Recontainer = scp079Recontainer;
+ Attacker = Player.Get(scp079Recontainer._activatorGlass.LastAttacker);
+ IsAutomatic = scp079Recontainer._activatorGlass.LastAttacker.IsSet;
}
///
@@ -35,5 +41,20 @@ public RecontainedEventArgs(Player player)
///
public Scp079Role Scp079 { get; }
+
+ ///
+ /// Gets the instance that handle SCP-079 recontained proccess.
+ ///
+ public PlayerRoles.PlayableScps.Scp079.Scp079Recontainer Recontainer { get; }
+
+ ///
+ /// Gets the player who recontained SCP-079.
+ ///
+ public Player Attacker { get; }
+
+ ///
+ /// Gets a value indicating whether the recontainment has been made automatically or by triggering the process.
+ ///
+ public bool IsAutomatic { get; }
}
-}
\ No newline at end of file
+}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
index f40e1e874..be8cef0ce 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
@@ -36,7 +36,7 @@ public StartPryingGateEventArgs(Player player, PryableDoor gate, bool isAllowed
{
Player = player;
Scp096 = player.Role.As();
- Gate = Door.Get(gate).As();
+ Gate = Door.Get(gate);
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
index c3077777c..5dca87a99 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
@@ -33,7 +33,7 @@ public class UsingScp244EventArgs : IPlayerEvent, IDeniableEvent
///
public UsingScp244EventArgs(Scp244Item scp244, Player player, bool isAllowed = true)
{
- Scp244 = (Scp244)Item.Get(scp244);
+ Scp244 = Item.Get(scp244);
Player = player;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
index 5aabbd226..985e90337 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
@@ -17,7 +17,7 @@ namespace Exiled.Events.EventArgs.Scp330
///
/// Contains all information before a player drops a SCP-330 candy.
///
- public class DroppingScp330EventArgs : IPlayerEvent, IDeniableEvent
+ public class DroppingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEvent
{
///
/// Initializes a new instance of the class.
@@ -34,14 +34,17 @@ public class DroppingScp330EventArgs : IPlayerEvent, IDeniableEvent
public DroppingScp330EventArgs(Player player, Scp330Bag scp330, CandyKindID candy)
{
Player = player;
- Scp330 = (Scp330)Item.Get(scp330);
+ Scp330 = Item.Get(scp330);
Candy = candy;
}
///
- /// Gets or sets a value representing the being picked up.
+ /// Gets or sets a value representing the being picked up.
///
- public Scp330 Scp330 { get; set; }
+ public Scp330 Scp330 { get; set; } // Todo Remove set
+
+ ///
+ public Item Item => Scp330;
///
/// Gets or sets a value indicating whether or not the type of candy drop.
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs
index 3fa09415b..29beac357 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs
@@ -8,7 +8,7 @@
namespace Exiled.Events.EventArgs.Scp330
{
using API.Features;
-
+ using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Usables.Scp330;
@@ -16,16 +16,18 @@ namespace Exiled.Events.EventArgs.Scp330
///
/// Contains all information after a player has eaten SCP-330.
///
- public class EatenScp330EventArgs : IPlayerEvent
+ public class EatenScp330EventArgs : IPlayerEvent, IScp330Event
{
///
/// Initializes a new instance of the class.
///
/// .
+ /// .
/// .
- public EatenScp330EventArgs(Player player, ICandy candy)
+ public EatenScp330EventArgs(Player player, Scp330Bag scp330, ICandy candy)
{
Player = player;
+ Scp330 = (Scp330)Item.Get(scp330);
Candy = candy;
}
@@ -38,5 +40,11 @@ public EatenScp330EventArgs(Player player, ICandy candy)
/// Gets the player who has eaten SCP-330.
///
public Player Player { get; }
+
+ ///
+ public Scp330 Scp330 { get; }
+
+ ///
+ public Item Item => Scp330;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs
index d24a57ad4..825f8424b 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs
@@ -8,7 +8,7 @@
namespace Exiled.Events.EventArgs.Scp330
{
using API.Features;
-
+ using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Usables.Scp330;
@@ -16,17 +16,19 @@ namespace Exiled.Events.EventArgs.Scp330
///
/// Contains all information before a player eats SCP-330.
///
- public class EatingScp330EventArgs : IPlayerEvent, IDeniableEvent
+ public class EatingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEvent
{
///
/// Initializes a new instance of the class.
///
/// .
- /// .
+ /// .
+ /// .
/// .
- public EatingScp330EventArgs(Player player, ICandy candy, bool isAllowed = true)
+ public EatingScp330EventArgs(Player player, Scp330Bag scp330, ICandy candy, bool isAllowed = true)
{
Player = player;
+ Scp330 = (Scp330)Item.Get(scp330);
Candy = candy;
IsAllowed = isAllowed;
}
@@ -45,5 +47,11 @@ public EatingScp330EventArgs(Player player, ICandy candy, bool isAllowed = true)
/// Gets the player who's eating SCP-330.
///
public Player Player { get; }
+
+ ///
+ public Scp330 Scp330 { get; }
+
+ ///
+ public Item Item => Scp330;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
index 30cd0a1ac..b87238842 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
@@ -8,15 +8,16 @@
namespace Exiled.Events.EventArgs.Scp330
{
using API.Features;
-
+ using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Usables.Scp330;
+ using YamlDotNet.Core.Tokens;
///
/// Contains all information before a player interacts with SCP-330.
///
- public class InteractingScp330EventArgs : IPlayerEvent, IDeniableEvent
+ public class InteractingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEvent
{
///
/// Initializes a new instance of the class.
@@ -30,6 +31,7 @@ public class InteractingScp330EventArgs : IPlayerEvent, IDeniableEvent
public InteractingScp330EventArgs(Player player, int usage)
{
Player = player;
+ Scp330 = Scp330Bag.TryGetBag(player.ReferenceHub, out Scp330Bag scp330Bag) ? (Scp330)Item.Get(scp330Bag) : null;
Candy = Scp330Candies.GetRandom();
UsageCount = usage;
ShouldSever = usage >= 2;
@@ -60,5 +62,13 @@ public InteractingScp330EventArgs(Player player, int usage)
/// Gets the triggering the event.
///
public Player Player { get; }
+
+ ///
+ /// This value can be null.
+ public Scp330 Scp330 { get; }
+
+ ///
+ /// This value can be null.
+ public Item Item => Scp330;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
new file mode 100644
index 000000000..107a7c869
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
@@ -0,0 +1,51 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Scp939
+{
+ using API.Features;
+ using API.Features.Hazards;
+ using Interfaces;
+ using PlayerRoles.PlayableScps.Scp939;
+
+ using Scp939Role = API.Features.Roles.Scp939Role;
+
+ ///
+ /// Contains all information after SCP-939 used its amnestic cloud ability.
+ ///
+ public class PlacedAmnesticCloudEventArgs : IScp939Event
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PlacedAmnesticCloudEventArgs(ReferenceHub hub, Scp939AmnesticCloudInstance cloud)
+ {
+ Player = Player.Get(hub);
+ AmnesticCloud = new AmnesticCloudHazard(cloud);
+ Scp939 = Player.Role.As();
+ }
+
+ ///
+ /// Gets the player who's controlling SCP-939.
+ ///
+ public Player Player { get; }
+
+ ///
+ /// Gets the instance.
+ ///
+ public AmnesticCloudHazard AmnesticCloud { get; }
+
+ ///
+ public Scp939Role Scp939 { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs
new file mode 100644
index 000000000..4aaa2b363
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs
@@ -0,0 +1,43 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Server
+{
+ using System.Collections.Generic;
+ using System.Linq;
+
+ using Exiled.API.Features;
+ using Exiled.Events.EventArgs.Interfaces;
+ using Respawning;
+
+ ///
+ /// Contains all information after team spawns.
+ ///
+ public class RespawnedTeamEventArgs : IExiledEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public RespawnedTeamEventArgs(SpawnableTeamType team, IEnumerable hubs)
+ {
+ Players = hubs.Select(Player.Get);
+ Team = team;
+ }
+
+ ///
+ /// Gets the list of spawned players.
+ ///
+ public IEnumerable Players { get; }
+
+ ///
+ /// Gets the spawned team.
+ ///
+ public SpawnableTeamType Team { get; }
+ }
+}
diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs
index 178071bb7..4c5fcabc5 100644
--- a/EXILED/Exiled.Events/Events.cs
+++ b/EXILED/Exiled.Events/Events.cs
@@ -21,6 +21,7 @@ namespace Exiled.Events
using PlayerRoles.Ragdolls;
using PlayerRoles.RoleAssign;
using PluginAPI.Events;
+ using Respawning;
using UnityEngine.SceneManagement;
///
@@ -70,7 +71,7 @@ public override void OnEnabled()
Handlers.Map.ChangedIntoGrenade += Handlers.Internal.ExplodingGrenade.OnChangedIntoGrenade;
CharacterClassManager.OnRoundStarted += Handlers.Server.OnRoundStarted;
-
+ RespawnManager.ServerOnRespawned += Handlers.Server.OnRespawnedTeam;
InventorySystem.InventoryExtensions.OnItemAdded += Handlers.Player.OnItemAdded;
InventorySystem.InventoryExtensions.OnItemRemoved += Handlers.Player.OnItemRemoved;
@@ -105,7 +106,7 @@ public override void OnDisabled()
InventorySystem.InventoryExtensions.OnItemAdded -= Handlers.Player.OnItemAdded;
InventorySystem.InventoryExtensions.OnItemRemoved -= Handlers.Player.OnItemRemoved;
-
+ RespawnManager.ServerOnRespawned -= Handlers.Server.OnRespawnedTeam;
RagdollManager.OnRagdollSpawned -= Handlers.Internal.RagdollList.OnSpawnedRagdoll;
RagdollManager.OnRagdollRemoved -= Handlers.Internal.RagdollList.OnRemovedRagdoll;
ItemPickupBase.OnPickupAdded -= Handlers.Internal.PickupEvent.OnSpawnedPickup;
diff --git a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
index 1b1dadf46..f75f2e91f 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
@@ -48,7 +48,7 @@ public static void OnMapGenerated()
Map.ClearCache();
PrefabHelper.LoadPrefabs();
- // TODO: Fix For (https://trello.com/c/cUwpZDLs/5003-config-teamrespawnqueue-in-configgameplay-is-not-working-as-expected)
+ // TODO: Fix For (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/377)
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);
diff --git a/EXILED/Exiled.Events/Handlers/Internal/Round.cs b/EXILED/Exiled.Events/Handlers/Internal/Round.cs
index 400eb044c..8f9bd58b2 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/Round.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/Round.cs
@@ -86,7 +86,7 @@ public static void OnVerified(VerifiedEventArgs ev)
{
RoleAssigner.CheckLateJoin(ev.Player.ReferenceHub, ClientInstanceMode.ReadyClient);
- // TODO: Remove if this has been fixed for https://trello.com/c/CzPD304L/5983-networking-blackout-is-not-synchronized-for-the-new-players
+ // TODO: Remove if this has been fixed for https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/52
foreach (Room room in Room.List.Where(current => current.AreLightsOff))
{
ev.Player.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkLightsEnabled), true);
diff --git a/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs b/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs
index 9bcb75cc8..4ecdc5f93 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs
@@ -8,7 +8,7 @@
namespace Exiled.Events.Handlers.Internal
{
using API.Features;
-
+ using Exiled.API.Features.Toys;
using UnityEngine.SceneManagement;
#pragma warning disable SA1611 // Element parameters should be documented
@@ -35,7 +35,7 @@ public static void OnSceneUnloaded(Scene _)
{
Player.UserIdsCache.Clear();
Player.Dictionary.Clear();
- Map.ToysValue.Clear();
+ AdminToy.BaseToAdminToy.Clear();
}
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs
index aca07ce6e..016bdf644 100644
--- a/EXILED/Exiled.Events/Handlers/Player.cs
+++ b/EXILED/Exiled.Events/Handlers/Player.cs
@@ -223,6 +223,11 @@ public class Player
///
public static Event RemovingHandcuffs { get; set; } = new();
+ ///
+ /// Invoked after freeing a handcuffed .
+ ///
+ public static Event RemovedHandcuffs { get; set; } = new();
+
///
/// Invoked before a escapes.
///
@@ -704,6 +709,12 @@ public class Player
/// The instance.
public static void OnRemovingHandcuffs(RemovingHandcuffsEventArgs ev) => RemovingHandcuffs.InvokeSafely(ev);
+ ///
+ /// Called after freeing a handcuffed .
+ ///
+ /// The instance.
+ public static void OnRemovedHandcuffs(RemovedHandcuffsEventArgs ev) => RemovedHandcuffs.InvokeSafely(ev);
+
///
/// Called before a escapes.
///
diff --git a/EXILED/Exiled.Events/Handlers/Scp939.cs b/EXILED/Exiled.Events/Handlers/Scp939.cs
index 14d1245c6..e82e94a80 100644
--- a/EXILED/Exiled.Events/Handlers/Scp939.cs
+++ b/EXILED/Exiled.Events/Handlers/Scp939.cs
@@ -32,6 +32,11 @@ public static class Scp939
///
public static Event PlacingAmnesticCloud { get; set; } = new();
+ ///
+ /// Invoked after SCP-939 used its amnestic cloud ability.
+ ///
+ public static Event PlacedAmnesticCloud { get; set; } = new();
+
///
/// Invoked before SCP-939 plays a stolen voice.
///
@@ -81,6 +86,12 @@ public static class Scp939
/// The instance.
public static void OnPlacingAmnesticCloud(PlacingAmnesticCloudEventArgs ev) => PlacingAmnesticCloud.InvokeSafely(ev);
+ ///
+ /// Called after SCP-939 used its amnestic cloud ability.
+ ///
+ /// The instance.
+ public static void OnPlacedAmnesticCloud(PlacedAmnesticCloudEventArgs ev) => PlacedAmnesticCloud.InvokeSafely(ev);
+
///
/// Called before SCP-939 plays a stolen voice.
///
diff --git a/EXILED/Exiled.Events/Handlers/Server.cs b/EXILED/Exiled.Events/Handlers/Server.cs
index 1b3d0481a..75be4d81c 100644
--- a/EXILED/Exiled.Events/Handlers/Server.cs
+++ b/EXILED/Exiled.Events/Handlers/Server.cs
@@ -7,6 +7,9 @@
namespace Exiled.Events.Handlers
{
+ using System.Collections.Generic;
+
+ using Respawning;
#pragma warning disable SA1623 // Property summary documentation should match accessors
using Exiled.Events.EventArgs.Player;
@@ -53,6 +56,11 @@ public static class Server
///
public static Event RespawningTeam { get; set; } = new();
+ ///
+ /// Invoked after team spawns.
+ ///
+ public static Event RespawnedTeam { get; set; } = new();
+
///
/// Invoked before adding an unit name.
///
@@ -142,6 +150,13 @@ public static class Server
/// The instance.
public static void OnRespawningTeam(RespawningTeamEventArgs ev) => RespawningTeam.InvokeSafely(ev);
+ ///
+ /// Called after team spawns.
+ ///
+ ///
+ ///
+ public static void OnRespawnedTeam(SpawnableTeamType teamType, List hubs) => RespawnedTeam.InvokeSafely(new RespawnedTeamEventArgs(teamType, hubs));
+
///
/// Called before adding an unit name.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs b/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
index 3e49274a0..17394e444 100644
--- a/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Events.Item
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features;
@@ -67,10 +68,8 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- Label continueLabel = generator.DefineLabel();
-
LocalBuilder pickup = generator.DeclareLocal(typeof(ItemPickupBase));
- int index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Dup);
-
- newInstructions.RemoveRange(index, 1);
-
- int offset = 1;
- index = newInstructions.FindIndex(i => i.opcode == OpCodes.Dup) + offset;
- newInstructions.InsertRange(index, new CodeInstruction[]
- {
- new(OpCodes.Dup),
- new(OpCodes.Stloc_S, pickup.LocalIndex),
- });
+ Label continueLabel = generator.DefineLabel();
- offset = -2;
- index = newInstructions.FindIndex(i => i.Calls(Method(typeof(NetworkServer), nameof(NetworkServer.Spawn), new[] { typeof(GameObject), typeof(NetworkConnection) }))) + offset;
+ int offset = -2;
+ int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(NetworkServer), nameof(NetworkServer.Spawn), new[] { typeof(GameObject), typeof(NetworkConnection) }))) + offset;
newInstructions.InsertRange(index, new[]
{
- new CodeInstruction(OpCodes.Ldsfld, Field(typeof(Scp244Spawner), nameof(Scp244Spawner.CompatibleRooms))).MoveLabelsFrom(newInstructions[index]),
+ // save Pickup from the stack
+ new CodeInstruction(OpCodes.Stloc_S, pickup.LocalIndex).MoveLabelsFrom(newInstructions[index]),
+
+ // Scp244Spawner.CompatibleRooms[num]
+ new(OpCodes.Ldsfld, Field(typeof(Scp244Spawner), nameof(Scp244Spawner.CompatibleRooms))),
new(OpCodes.Ldloc_0),
new(OpCodes.Callvirt, PropertyGetter(typeof(List), "Item")),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
- new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ItemPickupBase) })),
+ // scp244DeployablePickup
+ new(OpCodes.Ldloc_2),
+ // Scp244SpawningEventArgs ev = new(Room, Scp244DeployablePickup)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(Scp244SpawningEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnScp244Spawning))),
+ // if (ev.IsAllowed) goto continueLabel;
new(OpCodes.Call, PropertyGetter(typeof(Scp244SpawningEventArgs), nameof(Scp244SpawningEventArgs.IsAllowed))),
new(OpCodes.Brtrue_S, continueLabel),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
+ // scp244DeployablePickup.gameObject.Destroy()
+ // return;
+ new(OpCodes.Ldloc_2),
new(OpCodes.Callvirt, PropertyGetter(typeof(ItemPickupBase), nameof(ItemPickupBase.gameObject))),
new(OpCodes.Call, Method(typeof(NetworkServer), nameof(NetworkServer.Destroy))),
new(OpCodes.Ret),
- new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
+ // load pickup back into the stack
+ new CodeInstruction(OpCodes.Ldloc_S, pickup.LocalIndex).WithLabels(continueLabel),
});
for (int z = 0; z < newInstructions.Count; z++)
diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
index ff3cd5762..e65ced6fb 100644
--- a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
@@ -40,6 +40,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.IsLdarg(0));
newInstructions[lastIndex].labels.Add(doorSpawn);
+ // Replace
+ // "base.RegisterUnspawnedObject(doorNametagExtension.TargetDoor, itemPickupBase.gameObject);"
+ // with "base.RegisterUnspawnedObject(ev.Door.Base, itemPickupBase.gameObject);"
offset = -1;
index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldfld) + offset;
@@ -122,7 +123,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- int e = 0;
+ LocalBuilder fpcRole = generator.DeclareLocal(typeof(FpcStandardRoleBase));
+
+ // replace HumanRole to FpcStandardRoleBase
+ newInstructions.Find(x => x.opcode == OpCodes.Isinst).operand = typeof(FpcStandardRoleBase);
+
+ // after this index all invalid exit are considered Custom
+ int customExit = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldarg_0);
for (int i = 0; i < newInstructions.Count; i++)
{
- CodeInstruction codeInstruction = newInstructions[i];
- if (codeInstruction.opcode == OpCodes.Ldc_I4_0)
- {
- e++;
- if (e > 3)
- {
- newInstructions[i].opcode = OpCodes.Ldc_I4_5;
- }
- }
+ OpCode opcode = newInstructions[i].opcode;
+ if (opcode == OpCodes.Stloc_0)
+ newInstructions[i] = new CodeInstruction(OpCodes.Stloc_S, fpcRole.LocalIndex).WithLabels(newInstructions[i].labels);
+ else if (opcode == OpCodes.Ldloc_0)
+ newInstructions[i] = new CodeInstruction(OpCodes.Ldloc_S, fpcRole.LocalIndex).WithLabels(newInstructions[i].labels);
+ else if (opcode == OpCodes.Ldc_I4_0 && i > customExit)
+ newInstructions[i].opcode = OpCodes.Ldc_I4_5;
}
for (int z = 0; z < newInstructions.Count; z++)
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs b/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs
index 6ebd931f4..ff0dceace 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Events.Player
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -69,7 +70,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(API.Features.Items.Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Isinst, typeof(Firearm)),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, firearm.LocalIndex),
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs b/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs
index 6f17fad14..5acbce087 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -34,6 +34,8 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
Label continueLabel = generator.DefineLabel();
+ Label skipHealing = generator.DefineLabel();
+ Label skipHealed = generator.DefineLabel();
LocalBuilder ev = generator.DeclareLocal(typeof(HealingEventArgs));
LocalBuilder player = generator.DeclareLocal(typeof(Player));
@@ -48,10 +50,14 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable
/// Patches .
- /// Adds the and events.
+ /// Adds the , , and events.
///
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Handcuffing))]
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovingHandcuffs))]
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovedHandcuffs))]
[HarmonyPatch(typeof(DisarmingHandlers), nameof(DisarmingHandlers.ServerProcessDisarmMessage))]
internal static class ProcessDisarmMessage
{
@@ -46,6 +48,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
}
+
+ ///
+ /// Patches .
+ /// Invokes and event.
+ ///
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovingHandcuffs))]
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovedHandcuffs))]
+ [HarmonyPatch(typeof(DisarmedPlayers), nameof(DisarmedPlayers.ValidateEntry))]
+ internal static class Uncuff
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+ Label returnLabel = generator.DefineLabel();
+
+ int offset = 2;
+ int index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(Method(typeof(ReferenceHub), nameof(ReferenceHub.TryGetHubNetID)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ offset = 5;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerRoles.PlayerRoleManager), nameof(PlayerRoles.PlayerRoleManager.CurrentRole)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ offset = 3;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(UnityEngine.Vector3), nameof(UnityEngine.Vector3.sqrMagnitude)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.OutOfRange
+ new(OpCodes.Ldc_I4_1),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.OutOfRange, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.OutOfRange)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ newInstructions[newInstructions.Count - 2].labels.Add(returnLabel);
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs b/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
index 585457a43..ce6697b5d 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
@@ -7,16 +7,20 @@
namespace Exiled.Events.Patches.Events.Player
{
+#pragma warning disable SA1402 // File may only contain a single type
+#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
using System;
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
using API.Features;
+ using API.Features.Pools;
using CentralAuth;
using Exiled.API.Extensions;
using Exiled.Events.EventArgs.Player;
-
using HarmonyLib;
-#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
+ using static HarmonyLib.AccessTools;
///
/// Patches .
@@ -25,12 +29,16 @@ namespace Exiled.Events.Patches.Events.Player
[HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.FinalizeAuthentication))]
internal static class Verified
{
- private static void Postfix(PlayerAuthenticationManager __instance)
+ ///
+ /// Called after the player has been verified.
+ ///
+ /// The player's hub.
+ internal static void PlayerVerified(ReferenceHub hub)
{
- if (!Player.UnverifiedPlayers.TryGetValue(__instance._hub.gameObject, out Player player))
- Joined.CallEvent(__instance._hub, out player);
+ if (!Player.UnverifiedPlayers.TryGetValue(hub.gameObject, out Player player))
+ Joined.CallEvent(hub, out player);
- Player.Dictionary.Add(__instance._hub.gameObject, player);
+ Player.Dictionary.Add(hub.gameObject, player);
player.IsVerified = true;
player.RawUserId = player.UserId.GetRawUserId();
@@ -39,5 +47,41 @@ private static void Postfix(PlayerAuthenticationManager __instance)
Handlers.Player.OnVerified(new VerifiedEventArgs(player));
}
+
+ private static void Postfix(PlayerAuthenticationManager __instance)
+ {
+ PlayerVerified(__instance._hub);
+ }
+ }
+
+ ///
+ /// Patches .
+ /// Adds the event during offline mode.
+ ///
+ [HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.UserCode_CmdSetNick__String))]
+ internal static class VerifiedOfflineMode
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SyncServerCmdBinding)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Verified.PlayerVerified(this._hub);
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(NicknameSync), nameof(NicknameSync._hub))),
+ new CodeInstruction(OpCodes.Call, Method(typeof(Verified), nameof(Verified.PlayerVerified))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs b/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
index d1458e0fd..2eab40acc 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
@@ -25,11 +25,8 @@ namespace Exiled.Events.Patches.Events.Scp049
///
/// Patches .
/// Adds the event.
- /// Fix bug than Overwatch can get force respawn by Scp049
- /// Bug reported to NW https://trello.com/c/V0uHP2eV/5745-overwatch-overwatch-can-get-respawned-by-scp-049.
- /// The fix is directly inside the .
///
- // [EventPatch(typeof(Handlers.Scp049), nameof(Handlers.Scp049.FinishingRecall))]
+ [EventPatch(typeof(Handlers.Scp049), nameof(Handlers.Scp049.FinishingRecall))]
[HarmonyPatch(typeof(Scp049ResurrectAbility), nameof(Scp049ResurrectAbility.ServerComplete))]
internal static class FinishingRecall
{
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs b/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs
index 991ffc47e..3338c84ab 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs
@@ -37,9 +37,10 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.Calls(Method(typeof(Scp330Bag), nameof(Scp330Bag.ServerProcessPickup)))) + removeServerProcessOffset;
-
- newInstructions.RemoveRange(removeServerProcessIndex, 3);
-
- // Replace NW server process logic.
- newInstructions.InsertRange(
- removeServerProcessIndex,
- new[]
- {
- // ldarg.1 is already in the stack
-
- // ev.Candy
- new CodeInstruction(OpCodes.Ldloc, ev),
- new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.Candy))),
-
- // bag
- new CodeInstruction(OpCodes.Ldloca_S, 3),
-
- // ServerProcessPickup(ReferenceHub, CandyKindID, Scp330Bag)
- new CodeInstruction(OpCodes.Call, Method(typeof(InteractingScp330), nameof(ServerProcessPickup), new[] { typeof(ReferenceHub), typeof(CandyKindID), typeof(Scp330Bag).MakeByRefType() })),
- });
-
// This is to find the location of RpcMakeSound to remove the original code and add a new sever logic structure (Start point)
- int addShouldSeverOffset = 1;
+ int addShouldSeverOffset = -1;
int addShouldSeverIndex = newInstructions.FindLastIndex(
instruction => instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
- // This is to find the location of the next return (End point)
- int includeSameLine = 1;
- int nextReturn = newInstructions.FindIndex(addShouldSeverIndex, instruction => instruction.opcode == OpCodes.Ret) + includeSameLine;
- Label originalLabel = newInstructions[addShouldSeverIndex].ExtractLabels()[0];
-
- // Remove original code from after RpcMakeSound to next return and then fully replace it.
- newInstructions.RemoveRange(addShouldSeverIndex, nextReturn - addShouldSeverIndex);
-
- addShouldSeverIndex = newInstructions.FindLastIndex(
- instruction => instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
+ int serverEffectLocationStart = -1;
+ int enableEffect = newInstructions.FindLastIndex(
+ instruction => instruction.LoadsField(Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
+ newInstructions[enableEffect].WithLabels(enableEffectLabel);
newInstructions.InsertRange(
addShouldSeverIndex,
- new CodeInstruction[]
+ new[]
{
// if (!ev.ShouldSever)
// goto shouldNotSever;
- new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex).WithLabels(originalLabel),
+ new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.ShouldSever))),
new(OpCodes.Brfalse, shouldNotSever),
-
- // ev.Player.EnableEffect("SevereHands", 1, 0f, false)
- new(OpCodes.Ldloc, ev.LocalIndex),
- new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.Player))),
- new(OpCodes.Ldstr, nameof(SeveredHands)),
- new(OpCodes.Ldc_I4_1),
- new(OpCodes.Ldc_R4, 0f),
- new(OpCodes.Ldc_I4_0),
- new(OpCodes.Callvirt, Method(typeof(Player), nameof(Player.EnableEffect), new[] { typeof(string), typeof(byte), typeof(float), typeof(bool) })),
- new(OpCodes.Pop),
-
- // return;
- new(OpCodes.Ret),
+ new(OpCodes.Br, enableEffectLabel),
});
// This will let us jump to the taken candies code and lock until ldarg_0, meaning we allow base game logic handle candy adding.
@@ -157,28 +109,5 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
-
- private static bool ServerProcessPickup(ReferenceHub player, CandyKindID candy, out Scp330Bag bag)
- {
- if (!Scp330Bag.TryGetBag(player, out bag))
- {
- player.inventory.ServerAddItem(ItemType.SCP330);
-
- if (!Scp330Bag.TryGetBag(player, out bag))
- return false;
-
- bag.Candies = new List { candy };
- bag.ServerRefreshBag();
-
- return true;
- }
-
- bool result = bag.TryAddSpecific(candy);
-
- if (bag.AcquisitionAlreadyReceived)
- bag.ServerRefreshBag();
-
- return result;
- }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
new file mode 100644
index 000000000..ac224990f
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
@@ -0,0 +1,73 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Scp939
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Scp939;
+ using Exiled.Events.Handlers;
+ using HarmonyLib;
+ using PlayerRoles.PlayableScps.Scp939;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches
+ /// to add the event.
+ ///
+ [EventPatch(typeof(Scp939), nameof(Scp939.PlacedAmnesticCloud))]
+ [HarmonyPatch(typeof(Scp939AmnesticCloudAbility), nameof(Scp939AmnesticCloudAbility.OnStateEnabled))]
+ internal static class PlacedAmnesticCloud
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder cloud = generator.DeclareLocal(typeof(Scp939AmnesticCloudInstance));
+
+ const int offset = -2;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(Scp939AmnesticCloudInstance), nameof(Scp939AmnesticCloudInstance.ServerSetup)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Scp939AmnesticCloudInstance cloud = Object.Instantiate(this._instancePrefab)
+ new CodeInstruction(OpCodes.Dup),
+ new CodeInstruction(OpCodes.Stloc_S, cloud),
+ });
+
+ index = newInstructions.Count - 1;
+
+ // Scp939.OnPlacedAmnesticCloud(new PlacedAmnesticCloudEventArgs(this.Owner, cloud));
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // this.Owner
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(Scp939AmnesticCloudAbility), nameof(Scp939AmnesticCloudAbility.Owner))),
+
+ // cloud
+ new CodeInstruction(OpCodes.Ldloc_S, cloud),
+
+ // Scp939.OnPlacedAmnesticCloud(new PlacedAmnesticCloudEventArgs(this.Owner, cloud));
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PlacedAmnesticCloudEventArgs))[0]),
+ new(OpCodes.Call, Method(typeof(Scp939), nameof(Scp939.OnPlacedAmnesticCloud))),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
index bba54c92e..d672d1831 100644
--- a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
@@ -40,8 +40,8 @@ private static IEnumerable Transpiler(IEnumerable instruction.opcode == OpCodes.Newarr) + offset;
+ int offset = 2;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldarg_S && instruction.operand is byte and 4) + offset;
Label ret = generator.DefineLabel();
diff --git a/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs b/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
new file mode 100644
index 000000000..a924f6808
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
@@ -0,0 +1,55 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using HarmonyLib;
+ using InventorySystem;
+ using InventorySystem.Items;
+ using InventorySystem.Items.Armor;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/230 bug.
+ ///
+ [HarmonyPatch(typeof(BodyArmorUtils), nameof(BodyArmorUtils.RemoveEverythingExceedingLimits))]
+ internal static class ArmorDropPatch
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label continueLabel = generator.DefineLabel();
+ int continueIndex = newInstructions.FindIndex(x => x.Is(OpCodes.Call, Method(typeof(Dictionary.Enumerator), nameof(Dictionary.Enumerator.MoveNext)))) - 1;
+ newInstructions[continueIndex].WithLabels(continueLabel);
+
+ // before: if (keyValuePair.Value.Category != ItemCategory.Armor)
+ // after: if (keyValuePair.Value.Category != ItemCategory.Armor && keyValuePair.Value.Category != ItemCategory.None)
+ int index = newInstructions.FindIndex(x => x.Is(OpCodes.Ldc_I4_S, 9));
+
+ newInstructions.InsertRange(index, new CodeInstruction[]
+ {
+ // && keyValuePair.Value.Category != ItemCategory.None)
+ new(OpCodes.Ldloca_S, 4),
+ new(OpCodes.Call, PropertyGetter(typeof(KeyValuePair), nameof(KeyValuePair.Value))),
+ new(OpCodes.Ldfld, Field(typeof(ItemBase), nameof(ItemBase.Category))),
+ new(OpCodes.Ldc_I4_0),
+ new(OpCodes.Beq_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/Fixes/Fix106RegenerationWithScp244.cs b/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
new file mode 100644
index 000000000..33c54d5ad
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.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.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CustomPlayerEffects;
+ using HarmonyLib;
+ using InventorySystem.Items.Usables.Scp244.Hypothermia;
+ using PlayerRoles;
+ using PlayerRoles.PlayableScps.Scp106;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches the delegate.
+ /// Fix than SCP-106 regenerates slower in SCP-244 even if they are in stalk.
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/367).
+ ///
+ [HarmonyPatch(typeof(Hypothermia), nameof(Hypothermia.Update))]
+ internal class Fix106RegenerationWithScp244
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder scp106Role = generator.DeclareLocal(typeof(Scp106Role));
+ Label continueLabel = generator.DefineLabel();
+
+ int offset = 1;
+ int index = newInstructions.FindLastIndex(x => x.operand == (object)Method(typeof(SpawnProtected), nameof(SpawnProtected.CheckPlayer))) + offset;
+
+ Label skip = (Label)newInstructions[index].operand;
+
+ index += offset;
+
+ newInstructions[index].labels.Add(continueLabel);
+
+ newInstructions.InsertRange(index, new[]
+ {
+ // Scp106Role scp106Role = base.Hub.roleManager.CurrentRole as Scp106Role;
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Hub))),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.roleManager))),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(PlayerRoleManager), nameof(PlayerRoleManager.CurrentRole))),
+ new CodeInstruction(OpCodes.Isinst, typeof(Scp106Role)),
+ new CodeInstruction(OpCodes.Stloc_S, scp106Role.LocalIndex),
+
+ // if (scp106Role is null) goto continueLabel
+ new CodeInstruction(OpCodes.Ldloc_S, scp106Role.LocalIndex),
+ new CodeInstruction(OpCodes.Brfalse_S, continueLabel),
+
+ // if (!scp106Role.IsSubmerged) goto skip
+ new CodeInstruction(OpCodes.Ldloc_S, scp106Role.LocalIndex),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.IsSubmerged))),
+ new CodeInstruction(OpCodes.Brtrue_S, skip),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs b/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
index 8c37c4f9a..e37fb8993 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Fixes
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features.Items;
@@ -58,7 +59,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Isinst, typeof(Throwable)),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, throwable.LocalIndex),
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs b/EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs
new file mode 100644
index 000000000..680e40608
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs
@@ -0,0 +1,61 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Player
+{
+#pragma warning disable IDE0060
+
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features;
+ using API.Features.Pools;
+
+ using HarmonyLib;
+ using InventorySystem.Items.Jailbird;
+ using Mirror;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches .
+ /// Bug reported to NW (https://trello.com/c/kyr3hV9B).
+ ///
+ [HarmonyPatch(typeof(JailbirdDeteriorationTracker), nameof(JailbirdDeteriorationTracker.Setup))]
+ internal static class Jailbird914CoarseFix
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ int offset = -1;
+ int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Blt_S) + offset;
+
+ List
[HarmonyPatch(typeof(FpcMotor), nameof(FpcMotor.UpdatePosition))]
#pragma warning disable SA1402 // File may only contain a single type
@@ -62,4 +64,4 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
}
-}
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs b/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
new file mode 100644
index 000000000..2c65c58a8
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
@@ -0,0 +1,45 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using Footprinting;
+ using HarmonyLib;
+ using Interactables.Interobjects.DoorUtils;
+ using InventorySystem;
+ using InventorySystem.Items.Firearms.Ammo;
+ using InventorySystem.Items.Pickups;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches delegate.
+ /// Fix than NW config "lock_gates_on_countdown"
+ /// reported https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/316.
+ ///
+ [HarmonyPatch(typeof(DoorEventOpenerExtension), nameof(DoorEventOpenerExtension.Trigger))]
+ internal class WarheadConfigLockGateFix
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ // replace Contains with StartWith
+ int index = newInstructions.FindIndex(x => x.operand == (object)Method(typeof(string), nameof(string.Contains), new[] { typeof(string) }));
+ newInstructions[index].operand = Method(typeof(string), nameof(string.StartsWith), new System.Type[] { typeof(string) });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
index 1b63ec329..4f1ce6e33 100644
--- a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
@@ -50,7 +50,7 @@ private static void Postfix(DoorVariant __instance)
foreach (DoorVariant subDoor in checkpoint.Base.SubDoors)
{
subDoor.RegisterRooms();
- BreakableDoor targetDoor = Door.Get(subDoor).Cast();
+ BreakableDoor targetDoor = Door.Get(subDoor);
targetDoor.ParentCheckpointDoor = checkpoint;
checkpoint.SubDoorsValue.Add(targetDoor);
diff --git a/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs b/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs
new file mode 100644
index 000000000..eb533862e
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs
@@ -0,0 +1,35 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+ using System;
+
+ using Exiled.API.Extensions;
+ using Exiled.API.Features;
+ using HarmonyLib;
+ using InventorySystem.Configs;
+ using UnityEngine;
+
+ ///
+ /// Patches the delegate.
+ /// Sync .
+ /// Changes to .
+ ///
+ [HarmonyPatch(typeof(InventoryLimits), nameof(InventoryLimits.GetAmmoLimit), new Type[] { typeof(ItemType), typeof(ReferenceHub) })]
+ internal static class GetCustomAmmoLimit
+ {
+#pragma warning disable SA1313
+ private static void Postfix(ItemType ammoType, ReferenceHub player, ref ushort __result)
+ {
+ if (!Player.TryGet(player, out Player ply) || !ply.CustomAmmoLimits.TryGetValue(ammoType.GetAmmoType(), out ushort limit))
+ return;
+
+ __result = (ushort)Mathf.Clamp(limit + __result - InventoryLimits.GetAmmoLimit(null, ammoType), ushort.MinValue, ushort.MaxValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs b/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs
new file mode 100644
index 000000000..8ceff7538
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs
@@ -0,0 +1,34 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+ using System;
+
+ using Exiled.API.Features;
+ using HarmonyLib;
+ using InventorySystem.Configs;
+ using UnityEngine;
+
+ ///
+ /// Patches the delegate.
+ /// Sync , .
+ /// Changes to .
+ ///
+ [HarmonyPatch(typeof(InventoryLimits), nameof(InventoryLimits.GetCategoryLimit), new Type[] { typeof(ItemCategory), typeof(ReferenceHub), })]
+ internal static class GetCustomCategoryLimit
+ {
+#pragma warning disable SA1313
+ private static void Postfix(ItemCategory category, ReferenceHub player, ref sbyte __result)
+ {
+ if (!Player.TryGet(player, out Player ply) || !ply.CustomCategoryLimits.TryGetValue(category, out sbyte limit))
+ return;
+
+ __result = (sbyte)Mathf.Clamp(limit + __result - InventoryLimits.GetCategoryLimit(null, category), sbyte.MinValue, sbyte.MaxValue);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs b/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
new file mode 100644
index 000000000..46c412dc3
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
@@ -0,0 +1,164 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+#pragma warning disable SA1402 // File may only contain a single type
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CentralAuth;
+ using HarmonyLib;
+ using PluginAPI.Core.Interfaces;
+ using PluginAPI.Events;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to add an @offline suffix to UserIds in Offline Mode.
+ ///
+ [HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.Start))]
+ internal static class OfflineModeIds
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = -1;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Call && instruction.OperandIs(PropertySetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ new CodeInstruction(OpCodes.Call, Method(typeof(OfflineModeIds), nameof(BuildUserId))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static string BuildUserId(string userId) => $"{userId}@offline";
+ }
+
+ ///
+ /// Patches to add the player's UserId to the dictionary.
+ ///
+ [HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.Start))]
+ internal static class OfflineModePlayerIds
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label skipLabel = generator.DefineLabel();
+
+ const int offset = 1;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Call && instruction.OperandIs(PropertySetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId)))) + offset;
+
+ // if (!Player.PlayersUserIds.ContainsKey(this.UserId))
+ // Player.PlayersUserIds.Add(this.UserId, this._hub);
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // if (Player.PlayersUserIds.ContainsKey(this.UserId)) goto skip;
+ new(OpCodes.Ldsfld, Field(typeof(PluginAPI.Core.Player), nameof(PluginAPI.Core.Player.PlayersUserIds))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, PropertyGetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId))),
+ new(OpCodes.Callvirt, Method(typeof(Dictionary), nameof(Dictionary.ContainsKey))),
+ new(OpCodes.Brtrue_S, skipLabel),
+
+ // Player.PlayersUserIds.Add(this.UserId, this._hub);
+ new(OpCodes.Ldsfld, Field(typeof(PluginAPI.Core.Player), nameof(PluginAPI.Core.Player.PlayersUserIds))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, PropertyGetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager._hub))),
+ new(OpCodes.Callvirt, Method(typeof(Dictionary), nameof(Dictionary.Add))),
+
+ // skip:
+ new CodeInstruction(OpCodes.Nop).WithLabels(skipLabel),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches to prevent it from executing the event when the server is in offline mode.
+ ///
+ [HarmonyPatch(typeof(ReferenceHub), nameof(ReferenceHub.Start))]
+ internal static class OfflineModeReferenceHub
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt) + offset;
+
+ Label returnLabel = generator.DefineLabel();
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ new CodeInstruction(OpCodes.Br_S, returnLabel),
+ });
+
+ newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches to execute the event when the server is in offline mode.
+ ///
+ [HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.UserCode_CmdSetNick__String))]
+ internal static class OfflineModeJoin
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SyncServerCmdBinding)))) + offset;
+
+ // EventManager.ExecuteEvent(new PlayerJoinedEvent(this._hub));
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // EventManager.ExecuteEvent(new PlayerJoinedEvent(this._hub));
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(NicknameSync), nameof(NicknameSync._hub))),
+ new CodeInstruction(OpCodes.Call, Method(typeof(OfflineModeJoin), nameof(ExecuteNwEvent))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static void ExecuteNwEvent(ReferenceHub hub)
+ {
+ EventManager.ExecuteEvent(new PlayerJoinedEvent(hub));
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs b/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
index c76a8f247..fc319c474 100644
--- a/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
@@ -9,6 +9,7 @@ namespace Exiled.Events.Patches.Generic
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features.Pickups;
@@ -48,11 +49,11 @@ private static IEnumerable Transpiler(
{
// pickup = Pickup.Get(pickupBase);
new(OpCodes.Ldloc_0),
- new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ItemPickupBase) })),
+ new(OpCodes.Call, GetDeclaredMethods(typeof(Pickup)).First(x => !x.IsGenericMethod && x.Name is nameof(Pickup.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemPickupBase))),
// Item.Get(itemBase);
new(OpCodes.Ldarg_0),
- new(OpCodes.Call, Method(typeof(Item), nameof(Item.Get), new[] { typeof(ItemBase) })),
+ new(OpCodes.Call, GetDeclaredMethods(typeof(Item)).First(x => !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
// pickup.ReadItemInfo(item);
new(OpCodes.Callvirt, Method(typeof(Pickup), nameof(Pickup.ReadItemInfo))),
@@ -79,7 +80,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Pickup.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemPickupBase))),
new(OpCodes.Pop),
});
diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs b/EXILED/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs
deleted file mode 100644
index ddfb45adb..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp079API
-{
- using System.Collections.Generic;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp049;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp049Role = PlayerRoles.PlayableScps.Scp049.Scp049Role;
- using Scp049Role = API.Features.Roles.Scp049Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp049CallAbility), nameof(Scp049CallAbility.ServerProcessCmd))]
- internal class CallAbilityDuration
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp049Role));
-
- // replace "this.Duration.Trigger(20.0)" with "this.Duration.Trigger((Player.Get(base.Owner).Role as Scp049Role).CallAbilityDuration)"
- int offset = -1;
- int index = newInstructions.FindIndex(instruction => instruction.operand == (object)Method(typeof(AbilityCooldown), nameof(AbilityCooldown.Trigger))) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp049Role).CallAbilityDuration
- new(OpCodes.Isinst, typeof(Scp049Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp049Role), nameof(Scp049Role.CallAbilityDuration))),
- });
-
- 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/Generic/Scp049API/SenseAbilityBaseCooldown.cs b/EXILED/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityBaseCooldown.cs
deleted file mode 100644
index a70583b5f..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityBaseCooldown.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp079API
-{
- using System.Collections.Generic;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
-
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp049;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp049Role = PlayerRoles.PlayableScps.Scp049.Scp049Role;
- using Scp049Role = API.Features.Roles.Scp049Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp049SenseAbility), nameof(Scp049SenseAbility.ServerProcessKilledPlayer))]
- internal class SenseAbilityBaseCooldown
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp049Role));
-
- // replace "this.Duration.Trigger(40.0)" with "this.Duration.Trigger((Player.Get(base.Owner).Role as Scp049Role).SenseAbilityBaseCooldown)"
- int offset = -1;
- int index = newInstructions.FindIndex(instruction => instruction.operand == (object)Method(typeof(AbilityCooldown), nameof(AbilityCooldown.Trigger))) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp049Role).SenseAbilityBaseCooldown
- new(OpCodes.Isinst, typeof(Scp049Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp049Role), nameof(Scp049Role.SenseAbilityBaseCooldown))),
- });
-
- 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/Generic/Scp049API/SenseAbilityReducedCooldown.cs b/EXILED/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityReducedCooldown.cs
deleted file mode 100644
index 88c8333a1..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityReducedCooldown.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp079API
-{
- using System.Collections.Generic;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
-
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp049;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp049Role = PlayerRoles.PlayableScps.Scp049.Scp049Role;
- using Scp049Role = API.Features.Roles.Scp049Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp049SenseAbility), nameof(Scp049SenseAbility.ServerLoseTarget))]
- internal class SenseAbilityReducedCooldown
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp049Role));
-
- // replace "this.Duration.Trigger(40.0)" with "this.Duration.Trigger((Player.Get(base.Owner).Role as Scp049Role).SenseAbilityReducedCooldown)"
- int offset = -1;
- int index = newInstructions.FindIndex(instruction => instruction.operand == (object)Method(typeof(AbilityCooldown), nameof(AbilityCooldown.Trigger))) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp049Role).SenseAbilityReducedCooldown
- new(OpCodes.Isinst, typeof(Scp049Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp049Role), nameof(Scp049Role.SenseAbilityReducedCooldown))),
- });
-
- 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/Generic/Scp106API/CooldownReductionReward.cs b/EXILED/Exiled.Events/Patches/Generic/Scp106API/CooldownReductionReward.cs
deleted file mode 100644
index 4f509aad2..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp106API/CooldownReductionReward.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp106API
-{
- using System.Collections.Generic;
- using System.Reflection;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp106;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp106Role = PlayerRoles.PlayableScps.Scp106.Scp106Role;
- using Scp106Role = API.Features.Roles.Scp106Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp106Attack), nameof(Scp106Attack.ReduceSinkholeCooldown))]
- internal class CooldownReductionReward
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp106Role));
-
- // replace "base.ScpRole.Sinkhole.Cooldown.NextUse -= 5.0;
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // replace "base.ScpRole.Sinkhole.Cooldown.NextUse -= scp106Role.CooldownReductionReward;
- int offset = 0;
- int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldc_R4) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).CooldownReductionReward
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.CooldownReductionReward))),
- });
-
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
- }
-}
diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs b/EXILED/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs
deleted file mode 100644
index f6469919a..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp106API
-{
- using System.Collections.Generic;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp106;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp106Role = PlayerRoles.PlayableScps.Scp106.Scp106Role;
- using Scp106Role = API.Features.Roles.Scp106Role;
-
- ///
- /// Patches .
- /// Adds the , , property.
- ///
- // [HarmonyPatch(typeof(Scp106Attack), nameof(Scp106Attack.ServerShoot))]
- internal class CustomAttack
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp106Role));
-
- // replace "base.Vigor.VigorAmount += 0.3f;"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // "base.Vigor.VigorAmount += scp106Role.VigorCaptureReward;"
- int offset = 0;
- int index = newInstructions.FindIndex(instruction => instruction.operand == (object)Scp106Attack.VigorCaptureReward) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).AttackDamage
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.VigorCaptureReward))),
- });
-
- // replace "base.Vigor.VigorAmount += 0.3f;"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // "base.Vigor.VigorAmount += scp106Role.VigorCaptureReward;"
- offset = 0;
- index = newInstructions.FindIndex(instruction => instruction.operand == (object)Scp106Attack.VigorCaptureReward) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).AttackDamage
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.VigorCaptureReward))),
- });
-
- // replace "playerEffectsController.EnableEffect(20f, false);"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // "playerEffectsController.EnableEffect(scp106Role.CorrodingTime, false);"
- offset = 0;
- index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldc_R4) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).CorrodingTime
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.CorrodingTime))),
- });
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
- }
-}
diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs b/EXILED/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs
deleted file mode 100644
index d85ac0151..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp106API
-{
- using System.Collections.Generic;
- using System.Reflection;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp106;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp106Role = PlayerRoles.PlayableScps.Scp106.Scp106Role;
- using Scp106Role = API.Features.Roles.Scp106Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp106HuntersAtlasAbility), nameof(Scp106HuntersAtlasAbility.ServerProcessCmd))]
- internal class HunterAtlastCostPerMetter
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp106Role));
-
- // replace "num = (position2 - this._syncPos).MagnitudeIgnoreY() * 0.019f;"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // replace "num = (position2 - this._syncPos).MagnitudeIgnoreY() * scp106Role.HuntersAtlasCostPerMeter;"
- int offset = 0;
- int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldc_R4) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).HuntersAtlasCostPerMeter
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.HuntersAtlasCostPerMeter))),
- });
-
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
- }
-}
diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs b/EXILED/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs
deleted file mode 100644
index 519fef858..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp106API
-{
- using System.Collections.Generic;
- using System.Reflection;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp106;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp106Role = PlayerRoles.PlayableScps.Scp106.Scp106Role;
- using Scp106Role = API.Features.Roles.Scp106Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp106Attack), nameof(Scp106Attack.ReduceSinkholeCooldown))]
- internal class SinkholeAbilityCooldown
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp106Role));
-
- // replace "this.Cooldown.Trigger(20.0);"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // replace "this.Cooldown.Trigger(scp106Role.SinkholeCooldownDuration);"
- int offset = 0;
- int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldc_R4) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).SinkholeCooldownDuration
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.SinkholeCooldownDuration))),
- });
-
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
- }
-}
diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs b/EXILED/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs
deleted file mode 100644
index 38b7a9e53..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp106API
-{
- using System.Collections.Generic;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp106;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp106Role = PlayerRoles.PlayableScps.Scp106.Scp106Role;
- using Scp106Role = API.Features.Roles.Scp106Role;
-
- ///
- /// Patches .
- /// Adds the and property.
- ///
- // [HarmonyPatch(typeof(Scp106StalkAbility), nameof(Scp106StalkAbility.UpdateServerside))]
- internal class StalkVigorUse
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp106Role));
-
- // replace "float num = base.ScpRole.FpcModule.Motor.MovementDetected ? 0.09f : 0.06f;"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // "float num = base.ScpRole.FpcModule.Motor.MovementDetected ? scp106Role.VigorStalkCostMoving : scp106Role.VigorStalkCostStationary;"
- int offset = 0;
- int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldc_R4) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).VigorStalkCostMoving
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.VigorStalkCostMoving))),
- });
-
- offset = 0;
- index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldc_R4) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).VigorStalkCostStationary
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.VigorStalkCostStationary))),
- });
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
- }
-}
diff --git a/EXILED/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs b/EXILED/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs
deleted file mode 100644
index f14fd51d1..000000000
--- a/EXILED/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Exiled Team. All rights reserved.
-// Licensed under the CC BY-SA 3.0 license.
-//
-// -----------------------------------------------------------------------
-
-namespace Exiled.Events.Patches.Generic.Scp106API
-{
- using System.Collections.Generic;
- using System.Reflection.Emit;
-
- using API.Features.Pools;
- using Exiled.API.Features;
- using HarmonyLib;
- using PlayerRoles.PlayableScps.Scp106;
- using PlayerRoles.Subroutines;
-
- using static HarmonyLib.AccessTools;
-
- using BaseScp106Role = PlayerRoles.PlayableScps.Scp106.Scp106Role;
- using Scp106Role = API.Features.Roles.Scp106Role;
-
- ///
- /// Patches .
- /// Adds the property.
- ///
- // [HarmonyPatch(typeof(Scp106StalkAbility), nameof(Scp106StalkAbility.UpdateMovementState))]
- internal class VigorRegeneration
- {
- private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
- {
- List newInstructions = ListPool.Pool.Get(instructions);
-
- LocalBuilder scp049Role = generator.DeclareLocal(typeof(Scp106Role));
-
- // replace "base.Vigor.VigorAmount += 0.03f * Time.deltaTime;"
- // with
- // Scp106Role scp106Role = Player.Get(this.Owner).Role.As()
- // "base.Vigor.VigorAmount += scp106Role.VigorRegeneration * Time.deltaTime;"
- int offset = -4;
- int index = newInstructions.FindIndex(instruction => instruction.operand == (object)PropertySetter(typeof(Scp106VigorAbilityBase), nameof(Scp106VigorAbilityBase.VigorAmount))) + offset;
- newInstructions.RemoveAt(index);
-
- newInstructions.InsertRange(
- index,
- new CodeInstruction[]
- {
- // Player.Get(base.Owner).Role
- new(OpCodes.Ldarg_0),
- new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))),
- new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.Role))),
-
- // (Player.Get(base.Owner).Role as Scp106Role).VigorRegeneration
- new(OpCodes.Isinst, typeof(Scp106Role)),
- new(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.VigorRegeneration))),
- });
-
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
- }
-}
diff --git a/EXILED/Exiled.Installer/Program.cs b/EXILED/Exiled.Installer/Program.cs
index 34912cc51..559c17c82 100644
--- a/EXILED/Exiled.Installer/Program.cs
+++ b/EXILED/Exiled.Installer/Program.cs
@@ -42,7 +42,7 @@ internal enum PathResolution
internal static class Program
{
- private const long RepoID = 828620622;
+ private const long RepoID = 833723500;
private const string ExiledAssetName = "exiled.tar.gz";
// This is the lowest version the installer will check to install
@@ -303,4 +303,4 @@ private static Release FindRelease(CommandSettings args, IEnumerable re
return enumerable.First();
}
}
-}
\ No newline at end of file
+}
diff --git a/EXILED/Exiled.Loader/Updater.cs b/EXILED/Exiled.Loader/Updater.cs
index 6990c1812..fb0636570 100644
--- a/EXILED/Exiled.Loader/Updater.cs
+++ b/EXILED/Exiled.Loader/Updater.cs
@@ -31,7 +31,7 @@ namespace Exiled.Loader
///
internal sealed class Updater
{
- private const long REPOID = 828620622;
+ private const long REPOID = 833723500;
private const string INSTALLER_ASSET_NAME_LINUX = "Exiled.Installer-Linux";
private const string INSTALLER_ASSET_NAME_WIN = "Exiled.Installer-Win.exe";
@@ -332,4 +332,4 @@ private bool FindAsset(string assetName, Release release, out ReleaseAsset asset
return false;
}
}
-}
\ No newline at end of file
+}