diff --git a/.editorconfig b/.editorconfig index 77b992fde9..0777d2e4e1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,11 @@ ij_smart_tabs = false ij_visual_guides = none ij_wrap_on_typing = false +[*.cs] +csharp_style_var_for_built_in_types = false:error +csharp_style_var_when_type_is_apparent = false:error +csharp_style_var_elsewhere = false:error + # ReSharper properties resharper_csharp_max_line_length = 400 dotnet_diagnostic.SA1009.severity = none @@ -79,34 +84,6 @@ ij_json_spaces_within_braces = false ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false -[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] -ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 -ij_html_align_attributes = true -ij_html_align_text = false -ij_html_attribute_wrap = normal -ij_html_block_comment_add_space = false -ij_html_block_comment_at_first_column = true -ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p -ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot -ij_html_enforce_quotes = false -ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var -ij_html_keep_blank_lines = 2 -ij_html_keep_indents_on_empty_lines = false -ij_html_keep_line_breaks = true -ij_html_keep_line_breaks_in_text = true -ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span, pre, textarea -ij_html_line_comment_at_first_column = true -ij_html_new_line_after_last_attribute = never -ij_html_new_line_before_first_attribute = never -ij_html_quote_style = double -ij_html_remove_new_line_before_tags = br -ij_html_space_after_tag_name = false -ij_html_space_around_equality_in_attribute = false -ij_html_space_inside_empty_tag = false -ij_html_text_wrap = normal - [{*.markdown,*.md}] ij_markdown_force_one_space_after_blockquote_symbol = true ij_markdown_force_one_space_after_header_symbol = true diff --git a/.github/workflows/Resonance.yml b/.github/workflows/Resonance.yml new file mode 100644 index 0000000000..b6adfc4a30 --- /dev/null +++ b/.github/workflows/Resonance.yml @@ -0,0 +1,69 @@ +name: Exiled Resonance CI + +on: + push: + branches: + - apis-rework + pull_request: + branches: + - apis-rework + workflow_dispatch: + +env: + EXILED_REFERENCES_URL: https://misaka-zerotwo.github.io/SL-References/Dev.zip + EXILED_REFERENCES_PATH: ${{ github.workspace }}/References + EXILED_DLL_ARCHIVER_URL: https://github.com/Exiled-Team/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe + +jobs: + + build: + + runs-on: windows-latest + # Prevent double running for push & pull_request events from the main repo + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'Exiled-Team/Exiled' + + steps: + + - name: Setup .NET Core SDK + uses: actions/setup-dotnet@v1.7.2 + + - name: Setup Nuget + uses: iRebbok/setup-nuget@master + + - uses: actions/checkout@v2.3.4 + + - name: Get references + shell: pwsh + run: | + Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/References.zip + Expand-Archive -Path References.zip -DestinationPath ${{ env.EXILED_REFERENCES_PATH }} + + - name: Build + env: + EXILED_REFERENCES: ${{ env.EXILED_REFERENCES_PATH }} + shell: pwsh + run: | + ./build.ps1 -BuildNuGet + $File = (Get-ChildItem -Path . -Include 'EXILED.*.nupkg' -Recurse).Name + Out-File -FilePath ${{ github.env }} -InputObject "PackageFile=$File" -Encoding utf-8 -Append + + - name: Upload nuget package + uses: actions/upload-artifact@v2 + with: + name: ${{ env.PackageFile }} + path: ${{ env.PackageFile }} + + - name: Get references + shell: pwsh + run: | + Invoke-WebRequest -Uri ${{ env.EXILED_DLL_ARCHIVER_URL }} -OutFile ${{ github.workspace }}/EXILED-DLL-Archiver.exe + + - name: Packaging results as tar.gz + shell: pwsh + run: ./packaging.ps1 + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: Build Result + path: bin/Release/Exiled.tar.gz diff --git a/EXILED.props b/EXILED.props index 9271862ac3..ebc138a0f3 100644 --- a/EXILED.props +++ b/EXILED.props @@ -15,13 +15,14 @@ - 8.4.3 + 8.7.3 false 2.2.2 1.1.118 2.0.2 + 13.7.1 Copyright © $(Authors) 2020 - $([System.DateTime]::Now.ToString("yyyy")) Git diff --git a/Exiled.API/Enums/UEBranchType.cs b/Exiled.API/Enums/UEBranchType.cs index 43a4ef7425..5433abd074 100644 --- a/Exiled.API/Enums/UEBranchType.cs +++ b/Exiled.API/Enums/UEBranchType.cs @@ -7,7 +7,7 @@ namespace Exiled.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// All available branch environments. diff --git a/Exiled.API/Enums/UUKeypressTriggerType.cs b/Exiled.API/Enums/UUKeypressTriggerType.cs index 327c728b2a..6d5db36b61 100644 --- a/Exiled.API/Enums/UUKeypressTriggerType.cs +++ b/Exiled.API/Enums/UUKeypressTriggerType.cs @@ -7,7 +7,7 @@ namespace Exiled.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; #pragma warning disable SA1310 // Field names should not contain underscore diff --git a/Exiled.API/Exiled.API.csproj b/Exiled.API/Exiled.API.csproj index fc7f6f4c95..19a95ee8e6 100644 --- a/Exiled.API/Exiled.API.csproj +++ b/Exiled.API/Exiled.API.csproj @@ -14,12 +14,16 @@ <_Parameter1>Exiled.Events + + <_Parameter1>Exiled.Loader + + diff --git a/Exiled.API/Extensions/AnimationCurveExtensions.cs b/Exiled.API/Extensions/AnimationCurveExtensions.cs new file mode 100644 index 0000000000..a94e75556f --- /dev/null +++ b/Exiled.API/Extensions/AnimationCurveExtensions.cs @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Extensions +{ + using System; + using System.Collections.Generic; + + using UnityEngine; + + /// + /// A set of extensions for animation curves. + /// + public static class AnimationCurveExtensions + { + /// + /// Modify the curve with the amount used. + /// + /// The AnimationCurve to modify. + /// The multiplier number. + /// The new modified curve. + public static AnimationCurve Multiply(this AnimationCurve curve, float amount) + { + for (int i = 0; i < curve.length; i++) + curve.keys[i].value *= amount; + + return curve; + } + + /// + /// Modify the curve with the amount used. + /// + /// The AnimationCurve to modify. + /// The add number. + /// The new modified curve. + public static AnimationCurve Add(this AnimationCurve curve, float amount) + { + for (int i = 0; i < curve.length; i++) + curve.keys[i].value += amount; + + return curve; + } + } +} \ No newline at end of file diff --git a/Exiled.API/Extensions/CommonExtensions.cs b/Exiled.API/Extensions/CommonExtensions.cs deleted file mode 100644 index 49945467ec..0000000000 --- a/Exiled.API/Extensions/CommonExtensions.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.API.Extensions -{ - using System; - using System.Collections.Generic; - - using UnityEngine; - - /// - /// A set of extensions for common things. - /// - public static class CommonExtensions - { - /// - /// Gets a random value from an . - /// - /// to be used to get a random value. - /// Type of elements. - /// Returns a random value from . - [Obsolete("Use CollectionExtensions::Random(IEnumerable, Func) instead.", true)] - public static T GetRandomValue(this IEnumerable enumerable) => CollectionExtensions.Random(enumerable); - - /// - /// Gets a random value from an that matches the provided condition. - /// - /// to be used to get a random value. - /// Type of elements. - /// The condition to require. - /// Returns a random value from . - [Obsolete("Use CollectionExtensions::Random(IEnumerable, Func) instead.", true)] - public static T GetRandomValue(this IEnumerable enumerable, Func condition) => CollectionExtensions.Random(enumerable, condition); - - /// - /// Modify the curve with the amount used. - /// - /// The AnimationCurve to modify. - /// The multiplier number. - /// The new modfied curve. - public static AnimationCurve Multiply(this AnimationCurve curve, float amount) - { - for (int i = 0; i < curve.length; i++) - curve.keys[i].value *= amount; - - return curve; - } - - /// - /// Modify the curve with the amount used. - /// - /// The AnimationCurve to mofify. - /// The add number. - /// The new modfied curve. - public static AnimationCurve Add(this AnimationCurve curve, float amount) - { - for (int i = 0; i < curve.length; i++) - curve.keys[i].value += amount; - - return curve; - } - } -} \ No newline at end of file diff --git a/Exiled.API/Extensions/DamageTypeExtensions.cs b/Exiled.API/Extensions/DamageTypeExtensions.cs index c8f0975aca..8d9985f966 100644 --- a/Exiled.API/Extensions/DamageTypeExtensions.cs +++ b/Exiled.API/Extensions/DamageTypeExtensions.cs @@ -8,6 +8,7 @@ namespace Exiled.API.Extensions { using System.Collections.Generic; + using System.Linq; using Enums; @@ -20,37 +21,6 @@ namespace Exiled.API.Extensions /// public static class DamageTypeExtensions { - private static readonly Dictionary TranslationIdConversionInternal = new() - { - { DeathTranslations.Asphyxiated.Id, DamageType.Asphyxiation }, - { DeathTranslations.Bleeding.Id, DamageType.Bleeding }, - { DeathTranslations.Crushed.Id, DamageType.Crushed }, - { DeathTranslations.Decontamination.Id, DamageType.Decontamination }, - { DeathTranslations.Explosion.Id, DamageType.Explosion }, - { DeathTranslations.Falldown.Id, DamageType.Falldown }, - { DeathTranslations.Poisoned.Id, DamageType.Poison }, - { DeathTranslations.Recontained.Id, DamageType.Recontainment }, - { DeathTranslations.Scp049.Id, DamageType.Scp049 }, - { DeathTranslations.Scp096.Id, DamageType.Scp096 }, - { DeathTranslations.Scp173.Id, DamageType.Scp173 }, - { DeathTranslations.Scp207.Id, DamageType.Scp207 }, - { DeathTranslations.Scp939Lunge.Id, DamageType.Scp939 }, - { DeathTranslations.Scp939Other.Id, DamageType.Scp939 }, - { DeathTranslations.Scp3114Slap.Id, DamageType.Scp3114 }, - { DeathTranslations.Tesla.Id, DamageType.Tesla }, - { DeathTranslations.Unknown.Id, DamageType.Unknown }, - { DeathTranslations.Warhead.Id, DamageType.Warhead }, - { DeathTranslations.Zombie.Id, DamageType.Scp0492 }, - { DeathTranslations.BulletWounds.Id, DamageType.Firearm }, - { DeathTranslations.PocketDecay.Id, DamageType.PocketDimension }, - { DeathTranslations.SeveredHands.Id, DamageType.SeveredHands }, - { DeathTranslations.FriendlyFireDetector.Id, DamageType.FriendlyFireDetector }, - { DeathTranslations.UsedAs106Bait.Id, DamageType.FemurBreaker }, - { DeathTranslations.MicroHID.Id, DamageType.MicroHid }, - { DeathTranslations.Hypothermia.Id, DamageType.Hypothermia }, - { DeathTranslations.MarshmallowMan.Id, DamageType.Marshmallow }, - }; - private static readonly Dictionary TranslationConversionInternal = new() { { DeathTranslations.Asphyxiated, DamageType.Asphyxiation }, @@ -104,7 +74,7 @@ public static class DamageTypeExtensions /// /// Gets conversion information between s and s. /// - public static IReadOnlyDictionary TranslationIdConversion => TranslationIdConversionInternal; + public static IReadOnlyDictionary TranslationIdConversion { get; } = TranslationConversionInternal.ToDictionary(x => x.Key.Id, y => y.Value); /// /// Gets conversion information between s and s. @@ -199,28 +169,26 @@ public static DamageType GetDamageType(DamageHandlerBase damageHandlerBase) case ScpDamageHandler scpDamageHandler: { - DeathTranslation translation = DeathTranslations.TranslationsById[scpDamageHandler._translationId]; - if (translation.Id == DeathTranslations.PocketDecay.Id) + if (scpDamageHandler._translationId == DeathTranslations.PocketDecay.Id) return DamageType.Scp106; - return TranslationIdConversion.ContainsKey(translation.Id) - ? TranslationIdConversion[translation.Id] + return TranslationIdConversion.ContainsKey(scpDamageHandler._translationId) + ? TranslationIdConversion[scpDamageHandler._translationId] : DamageType.Scp; } case UniversalDamageHandler universal: { - DeathTranslation translation = DeathTranslations.TranslationsById[universal.TranslationId]; + if (TranslationIdConversion.ContainsKey(universal.TranslationId)) + return TranslationIdConversion[universal.TranslationId]; - if (TranslationIdConversion.ContainsKey(translation.Id)) - return TranslationIdConversion[translation.Id]; - - Log.Warn($"{nameof(DamageTypeExtensions)}.{nameof(damageHandlerBase)}: No matching {nameof(DamageType)} for {nameof(UniversalDamageHandler)} with ID {translation.Id}, type will be reported as {DamageType.Unknown}. Report this to EXILED Devs."); + Log.Warn($"{nameof(DamageTypeExtensions)}.{nameof(damageHandlerBase)}: No matching {nameof(DamageType)} for {nameof(UniversalDamageHandler)} with ID {universal.TranslationId}, type will be reported as {DamageType.Unknown}. Report this to EXILED Devs."); return DamageType.Unknown; } - } - return DamageType.Unknown; + default: + return DamageType.Unknown; + } } } } diff --git a/Exiled.API/Extensions/ItemExtensions.cs b/Exiled.API/Extensions/ItemExtensions.cs index 4c2e1dd2ec..2aa7c90fc0 100644 --- a/Exiled.API/Extensions/ItemExtensions.cs +++ b/Exiled.API/Extensions/ItemExtensions.cs @@ -96,6 +96,13 @@ public static ItemBase GetItemBase(this ItemType type) return itemBase; } + /// + /// Given an , returns the matching . + /// + /// The . + /// The , or if not found. + public static ItemPickupBase GetPickupBase(this ItemType type) => GetItemBase(type)?.PickupDropModel; + /// /// Given an , returns the matching , casted to . /// @@ -316,12 +323,5 @@ public static uint GetBaseCode(this FirearmType type) /// The to check. /// of the specified . public static ItemCategory GetCategory(this ItemType type) => GetItemBase(type).Category; - - /// - /// Given an , returns the matching . - /// - /// The . - /// The , or if not found. - public static ItemPickupBase GetPickupBase(this ItemType type) => GetItemBase(type)?.PickupDropModel; } } diff --git a/Exiled.API/Extensions/MathExtensions.cs b/Exiled.API/Extensions/MathExtensions.cs new file mode 100644 index 0000000000..980fac0267 --- /dev/null +++ b/Exiled.API/Extensions/MathExtensions.cs @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Extensions +{ + using System; + using System.Collections.Generic; + + using UnityEngine; + + /// + /// A set of extensions for mathematical operations. + /// + public static class MathExtensions + { + /// + /// Evaluates a probability. + /// + /// The probability to evaluate. + /// if the probability occurred, otherwise . + public static bool EvaluateProbability(this int probability) => UnityEngine.Random.Range(0, 101) <= probability; + + /// + /// Evaluates a probability. + /// + /// The probability to evaluate. + /// The minimum value to include in the range. + /// The maximum value to include in the range. + /// if the probability occurred, otherwise . + public static bool EvaluateProbability(this int probability, int minInclusive = 0, int maxInclusive = 100) => UnityEngine.Random.Range(minInclusive, ++maxInclusive) <= probability; + + /// + /// Evaluates a probability. + /// + /// The probability to evaluate. + /// if the probability occurred, otherwise . + public static bool EvaluateProbability(this float probability) => UnityEngine.Random.Range(0f, 100f) <= probability; + + /// + /// Evaluates a probability. + /// + /// The probability to evaluate. + /// The minimum value to include in the range. + /// The maximum value to include in the range. + /// if the probability occurred, otherwise . + public static bool EvaluateProbability(this float probability, float minInclusive = 0f, float maxInclusive = 100f) => UnityEngine.Random.Range(minInclusive, maxInclusive) <= probability; + } +} \ No newline at end of file diff --git a/Exiled.API/Extensions/MirrorExtensions.cs b/Exiled.API/Extensions/MirrorExtensions.cs index 1d25f89592..7baf72eb64 100644 --- a/Exiled.API/Extensions/MirrorExtensions.cs +++ b/Exiled.API/Extensions/MirrorExtensions.cs @@ -16,7 +16,7 @@ namespace Exiled.API.Extensions using System.Text; using Features; - using Features.Pools; + using Features.Core.Generic.Pools; using InventorySystem.Items.Firearms; diff --git a/Exiled.API/Extensions/QueueExtensions.cs b/Exiled.API/Extensions/QueueExtensions.cs index 625a5a7787..8a0382a8b2 100644 --- a/Exiled.API/Extensions/QueueExtensions.cs +++ b/Exiled.API/Extensions/QueueExtensions.cs @@ -10,7 +10,7 @@ namespace Exiled.API.Extensions using System; using System.Collections.Generic; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; /// /// extensions. diff --git a/Exiled.API/Extensions/StringExtensions.cs b/Exiled.API/Extensions/StringExtensions.cs index 69b184bc65..093824c640 100644 --- a/Exiled.API/Extensions/StringExtensions.cs +++ b/Exiled.API/Extensions/StringExtensions.cs @@ -14,7 +14,7 @@ namespace Exiled.API.Extensions using System.Text; using System.Text.RegularExpressions; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; /// /// A set of extensions for . diff --git a/Exiled.API/Features/Attributes/DefaultPlayerClassAttribute.cs b/Exiled.API/Features/Attributes/DefaultPlayerClassAttribute.cs new file mode 100644 index 0000000000..733f1960fc --- /dev/null +++ b/Exiled.API/Features/Attributes/DefaultPlayerClassAttribute.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Attributes +{ + using System; + + /// + /// This attribute determines whether the class which is being applied to should replace the default class. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public class DefaultPlayerClassAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether the type should enforce its authority, ignoring all other classes. + public DefaultPlayerClassAttribute(bool enforceAuthority = false) => EnforceAuthority = enforceAuthority; + + /// + /// Gets a value indicating whether the type should enforce its authority, ignoring all other classes. + /// + public bool EnforceAuthority { get; } + } +} \ No newline at end of file diff --git a/Exiled.API/Features/Camera.cs b/Exiled.API/Features/Camera.cs index 1e04e75acf..35dc53ee75 100644 --- a/Exiled.API/Features/Camera.cs +++ b/Exiled.API/Features/Camera.cs @@ -134,6 +134,7 @@ public class Camera : GameEntity, IWrapper, IWorldSpace /// /// The base camera. internal Camera(Scp079Camera camera079) + : base() { Base = camera079; Camera079ToCamera.Add(camera079, this); @@ -147,12 +148,12 @@ internal Camera(Scp079Camera camera079) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => Camera079ToCamera.Values; + public static new IReadOnlyCollection List => Camera079ToCamera.Values; /// - /// Gets a random . + /// Gets a randomly selected . /// - /// object. + /// A randomly selected object. public static Camera Random => List.Random(); /// @@ -165,11 +166,6 @@ internal Camera(Scp079Camera camera079) /// public Scp079Camera Base { get; } - /// - /// Gets the camera's . - /// - public Transform Transform => Base.transform; - /// /// Gets the camera's name. /// @@ -188,7 +184,7 @@ internal Camera(Scp079Camera camera079) /// /// Gets the camera's . /// - public ZoneType Zone => Room?.Zone ?? ZoneType.Unspecified; + public ZoneType Zone => Room ? Room.Zone : ZoneType.Unspecified; /// /// Gets the camera's . @@ -198,12 +194,12 @@ internal Camera(Scp079Camera camera079) /// /// Gets the camera's position. /// - public Vector3 Position => Base.Position; + public override Vector3 Position => Base.Position; /// /// Gets or sets the camera's rotation. /// - public Quaternion Rotation + public override Quaternion Rotation { get => Base._cameraAnchor.rotation; set => Base._cameraAnchor.rotation = value; @@ -261,8 +257,8 @@ public bool IsBeingUsed /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// @@ -308,8 +304,8 @@ public bool IsBeingUsed /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. /// if is not , or if is . public static bool TryGet(Func predicate, out IEnumerable result) => (result = Get(predicate)).Any(); diff --git a/Exiled.API/Features/Cassie.cs b/Exiled.API/Features/Cassie.cs index 1cbb11d75e..1e545161b8 100644 --- a/Exiled.API/Features/Cassie.cs +++ b/Exiled.API/Features/Cassie.cs @@ -11,7 +11,8 @@ namespace Exiled.API.Features using System.Linq; using System.Text; - using Exiled.API.Features.Pools; + using Exiled.API.Extensions; + using Exiled.API.Features.Core.Generic.Pools; using MEC; @@ -20,6 +21,8 @@ namespace Exiled.API.Features using PlayerStatsSystem; using Respawning; + using Subtitles; + using Utils.Networking; using CustomFirearmHandler = DamageHandlers.FirearmDamageHandler; using CustomHandlerBase = DamageHandlers.DamageHandlerBase; @@ -147,21 +150,20 @@ public static void ScpTermination(Player scp, DamageHandlerBase info) public static void CustomScpTermination(string scpName, CustomHandlerBase info) { string result = scpName; - if (info.Is(out MicroHidDamageHandler _)) - result += " SUCCESSFULLY TERMINATED BY AUTOMATIC SECURITY SYSTEM"; - else if (info.Is(out WarheadDamageHandler _)) - result += " SUCCESSFULLY TERMINATED BY ALPHA WARHEAD"; - else if (info.Is(out UniversalDamageHandler _)) - result += " LOST IN DECONTAMINATION SEQUENCE"; - else if (info.BaseIs(out CustomFirearmHandler firearmDamageHandler) && firearmDamageHandler.Attacker is Player attacker) - result += " CONTAINEDSUCCESSFULLY " + ConvertTeam(attacker.Role.Team, attacker.UnitName); - - // result += "To be changed"; - else - result += " SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED"; - - float num = AlphaWarheadController.TimeUntilDetonation <= 0f ? 3.5f : 1f; + + result += info.Base switch + { + UniversalDamageHandler universalDamageHandler when universalDamageHandler.TranslationId == DeathTranslations.Tesla.Id => " SUCCESSFULLY TERMINATED BY AUTOMATIC SECURITY SYSTEM", + UniversalDamageHandler universalDamageHandler when universalDamageHandler.TranslationId == DeathTranslations.Decontamination.Id => " LOST IN DECONTAMINATION SEQUENCE", + UniversalDamageHandler universalDamageHandler when universalDamageHandler.TranslationId == DeathTranslations.MarshmallowMan.Id => " TERMINATED BY MARSHMALLOW MAN", + WarheadDamageHandler => " SUCCESSFULLY TERMINATED BY ALPHA WARHEAD", + _ => info.Is(out CustomFirearmHandler firearmDamageHandler) && firearmDamageHandler.Attacker is Player attacker ? + " CONTAINEDSUCCESSFULLY " + ConvertTeam(attacker.Role.Team, attacker.UnitName) : " SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED" + }; + + float num = AlphaWarheadController.Detonated ? 3.5f : 1f; GlitchyMessage(result, UnityEngine.Random.Range(0.1f, 0.14f) * num, UnityEngine.Random.Range(0.07f, 0.08f) * num); + new SubtitleMessage(new SubtitlePart(SubtitleType.SCP, new string[] { scpName.RemoveSpaces() })).SendToAuthenticated(0); } /// diff --git a/Exiled.API/Features/Core/EItemBehaviour.cs b/Exiled.API/Features/Core/Behaviours/EItemBehaviour.cs similarity index 93% rename from Exiled.API/Features/Core/EItemBehaviour.cs rename to Exiled.API/Features/Core/Behaviours/EItemBehaviour.cs index 743fb34a1e..de1a1b4aa8 100644 --- a/Exiled.API/Features/Core/EItemBehaviour.cs +++ b/Exiled.API/Features/Core/Behaviours/EItemBehaviour.cs @@ -5,9 +5,9 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core +namespace Exiled.API.Features.Core.Behaviours { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Items; /// diff --git a/Exiled.API/Features/Core/EPickupBehaviour.cs b/Exiled.API/Features/Core/Behaviours/EPickupBehaviour.cs similarity index 93% rename from Exiled.API/Features/Core/EPickupBehaviour.cs rename to Exiled.API/Features/Core/Behaviours/EPickupBehaviour.cs index 902905d7d2..7848f84aeb 100644 --- a/Exiled.API/Features/Core/EPickupBehaviour.cs +++ b/Exiled.API/Features/Core/Behaviours/EPickupBehaviour.cs @@ -5,9 +5,9 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core +namespace Exiled.API.Features.Core.Behaviours { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Pickups; /// diff --git a/Exiled.API/Features/Core/EPlayerBehaviour.cs b/Exiled.API/Features/Core/Behaviours/EPlayerBehaviour.cs similarity index 93% rename from Exiled.API/Features/Core/EPlayerBehaviour.cs rename to Exiled.API/Features/Core/Behaviours/EPlayerBehaviour.cs index 358b07dec7..115e85d12e 100644 --- a/Exiled.API/Features/Core/EPlayerBehaviour.cs +++ b/Exiled.API/Features/Core/Behaviours/EPlayerBehaviour.cs @@ -5,10 +5,10 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core +namespace Exiled.API.Features.Core.Behaviours { using Exiled.API.Features; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// is a versatile component designed to enhance the functionality of playable characters. diff --git a/Exiled.API/Features/Core/TickComponent.cs b/Exiled.API/Features/Core/Components/TickComponent.cs similarity index 93% rename from Exiled.API/Features/Core/TickComponent.cs rename to Exiled.API/Features/Core/Components/TickComponent.cs index c0d0511b9b..7e65d93a3b 100644 --- a/Exiled.API/Features/Core/TickComponent.cs +++ b/Exiled.API/Features/Core/Components/TickComponent.cs @@ -5,13 +5,13 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core +namespace Exiled.API.Features.Core.Components { using System; using System.Collections.Generic; using System.Linq; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using MEC; @@ -23,7 +23,9 @@ public sealed class TickComponent : EObject /// /// The default fixed tick rate (60 per second). /// - public const float DefaultFixedTickRate = 0.016f; +#pragma warning disable SA1310 + public const float DEFAULT_FIXED_TICK_RATE = 0.016f; +#pragma warning restore SA1310 private readonly HashSet boundHandles; private readonly CoroutineHandle executeAllHandle; @@ -43,7 +45,7 @@ internal TickComponent() /// /// Gets or sets the current tick rate. /// - public float TickRate { get; set; } = DefaultFixedTickRate; + public float TickRate { get; set; } = DEFAULT_FIXED_TICK_RATE; /// /// Gets or sets a value indicating whether the can tick. diff --git a/Exiled.API/Features/Core/EActor.cs b/Exiled.API/Features/Core/EActor.cs index fdb75d5a09..aec70bcb0d 100644 --- a/Exiled.API/Features/Core/EActor.cs +++ b/Exiled.API/Features/Core/EActor.cs @@ -11,12 +11,13 @@ namespace Exiled.API.Features.Core using System.Collections.Generic; using System.Linq; + using Exiled.API.Extensions; + using Exiled.API.Features.Core.Components; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; - using Exiled.API.Features.Pools; using Exiled.API.Interfaces; using MEC; - using UnityEngine; /// @@ -27,7 +28,9 @@ public abstract class EActor : EObject, IEntity, IWorldSpace /// /// The default fixed tick rate. /// - public const float DefaultFixedTickRate = TickComponent.DefaultFixedTickRate; +#pragma warning disable SA1310 + public const float DEFAULT_FIXED_TICK_RATE = TickComponent.DEFAULT_FIXED_TICK_RATE; +#pragma warning restore SA1310 private readonly HashSet componentsInChildren = HashSetPool.Pool.Get(); private CoroutineHandle serverTick; @@ -42,7 +45,7 @@ protected EActor() { IsEditable = true; CanEverTick = true; - fixedTickRate = DefaultFixedTickRate; + fixedTickRate = DEFAULT_FIXED_TICK_RATE; PostInitialize(); Timing.CallDelayed(fixedTickRate, () => OnBeginPlay()); Timing.CallDelayed(fixedTickRate * 2, () => serverTick = Timing.RunCoroutine(ServerTick())); @@ -254,6 +257,147 @@ public IEnumerable AddComponents(IEnumerable types) yield return AddComponent(type); } + /// + public T RemoveComponent(string name = "") + where T : EActor + { + T comp = null; + + if (string.IsNullOrEmpty(name)) + { + if (!TryGetComponent(out comp)) + return null; + + comp.Base = null; + componentsInChildren.Remove(comp); + return comp; + } + + foreach (EActor actor in GetComponents()) + { + if (actor.Name != name) + continue; + + comp = actor.Cast(); + } + + return comp; + } + + /// + public T RemoveComponent(EActor actor, string name = "") + where T : EActor + { + T comp = null; + + if (string.IsNullOrEmpty(name)) + { + if (!TryGetComponent(out comp) || comp != actor) + return null; + + comp.Base = null; + componentsInChildren.Remove(comp); + return comp; + } + + foreach (EActor component in GetComponents()) + { + if (component.Name != name && component == actor) + continue; + + comp = component.Cast(); + } + + return comp; + } + + /// + public EActor RemoveComponent(Type type, string name = "") + { + EActor comp = null; + + if (string.IsNullOrEmpty(name)) + { + if (!TryGetComponent(type, out comp)) + return null; + + comp.Base = null; + componentsInChildren.Remove(comp); + return comp; + } + + foreach (EActor actor in GetComponents(type)) + { + if (actor.Name != name) + continue; + + comp = actor; + } + + return comp; + } + + /// + public EActor RemoveComponent(EActor actor, string name = "") + { + if (!componentsInChildren.Contains(actor)) + return null; + + if (string.IsNullOrEmpty(name)) + { + actor.Base = null; + componentsInChildren.Remove(actor); + return actor; + } + + foreach (EActor component in componentsInChildren) + { + if (component != actor || actor.Name != name) + continue; + + actor = component; + } + + return actor; + } + + /// + public IEnumerable RemoveComponentOfType(string name = "") + where T : EActor + { + IEnumerable components = GetComponents(); + + foreach (T comp in components) + RemoveComponent(comp, name); + + return components; + } + + /// + public IEnumerable RemoveComponentOfType(Type type, string name = "") + { + IEnumerable components = GetComponents(type); + + foreach (EActor comp in components) + RemoveComponent(comp, name); + + return components; + } + + /// + public void RemoveComponents(IEnumerable types) => types.ForEach(type => RemoveComponent(type)); + + /// + public void RemoveComponents(IEnumerable actors) => actors.ForEach(actor => RemoveComponent(actor)); + + /// + public void RemoveComponents(IEnumerable actors) + where T : EActor => actors.ForEach(actor => RemoveComponent(actor)); + + /// + public void RemoveComponents(IEnumerable types) + where T : EActor => types.ForEach(type => RemoveComponent(type)); + /// public EActor GetComponent(Type type) => ComponentsInChildren.FirstOrDefault(comp => type == comp.GetType()); diff --git a/Exiled.API/Features/Core/EObject.cs b/Exiled.API/Features/Core/EObject.cs index 4256883404..526809890e 100644 --- a/Exiled.API/Features/Core/EObject.cs +++ b/Exiled.API/Features/Core/EObject.cs @@ -14,7 +14,7 @@ namespace Exiled.API.Features.Core using Exiled.API.Features.Core.Attributes; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using UnityEngine; @@ -518,7 +518,7 @@ public static void DestroyAllObjectsOfType() /// Finds the active instances of type filtered based on a predicate. /// /// The type to look for. - /// The condition to satify. + /// The condition to satisfy. /// The corresponding active . public static T FindActiveObjectOfType(Func predicate) where T : EObject @@ -536,7 +536,7 @@ public static T FindActiveObjectOfType(Func predicate) /// Finds all the active instances of type filtered based on a predicate. /// /// The type to look for. - /// The condition to satify. + /// The condition to satisfy. /// A [] containing all the matching results. public static T[] FindActiveObjectsOfType(Func predicate) where T : EObject @@ -612,7 +612,7 @@ public static T[] FindActiveObjectsOfType(Type type) /// /// The type to look for. /// The type. - /// The condition to satify. + /// The condition to satisfy. /// A [] containing all the matching results. public static T[] FindActiveObjectsOfType(Type type, Func predicate) where T : EObject @@ -652,8 +652,8 @@ public static T[] FindActiveObjectsWithTagOfType(string tag) /// Finds all the active instances of type . /// /// The type to look for. - /// The condition to satify. - /// A [] containing all the elements that satify the condition. + /// The condition to satisfy. + /// A [] containing all the elements that satisfy the condition. public static T[] FindActiveObjectsOfType(Func predicate) where T : EObject { @@ -671,8 +671,8 @@ public static T[] FindActiveObjectsOfType(Func predicate) /// Finds all the active instances of type . /// /// The type to look for. - /// The condition to satify. - /// A [] containing all the elements that satify the condition. + /// The condition to satisfy. + /// A [] containing all the elements that satisfy the condition. public static T[] FindActiveObjectsOfType(Func predicate) where T : EObject { diff --git a/Exiled.API/Features/Core/GameEntity.cs b/Exiled.API/Features/Core/GameEntity.cs index 769c08a306..4085c423ff 100644 --- a/Exiled.API/Features/Core/GameEntity.cs +++ b/Exiled.API/Features/Core/GameEntity.cs @@ -11,17 +11,43 @@ namespace Exiled.API.Features.Core using System.Collections.Generic; using System.Linq; + using Exiled.API.Extensions; using Exiled.API.Features.Core.Interfaces; - + using Exiled.API.Interfaces; using UnityEngine; /// /// The base class which defines in-game entities. /// - public abstract class GameEntity : TypeCastObject, IEntity + public abstract class GameEntity : TypeCastObject, IEntity, IWorldSpace { + /// + /// The room's transform. + /// +#pragma warning disable SA1401 + protected Transform transform; +#pragma warning restore SA1401 + + private static readonly HashSet ActiveInstances = new(); private readonly HashSet componentsInChildren = new(); + /// + /// Initializes a new instance of the class. + /// + protected GameEntity() => ActiveInstances.Add(this); + + /// + /// Finalizes an instance of the class. + /// + ~GameEntity() => ActiveInstances.Remove(this); + + /// + /// Gets all active instances. + /// + /// This collection should be used sparingly and only if circumstances require it, due to its potentially large size. + /// + public static HashSet List => ActiveInstances; + /// public IReadOnlyCollection ComponentsInChildren => componentsInChildren; @@ -30,6 +56,67 @@ public abstract class GameEntity : TypeCastObject, IEntity /// public virtual GameObject GameObject { get; protected set; } + /// + /// Gets the . + /// + public virtual Transform Transform => transform ? transform : transform = GameObject.transform; + + /// + /// Gets or sets the position. + /// + public virtual Vector3 Position + { + get => Transform.position; + set => Transform.position = value; + } + + /// + /// Gets or sets the rotation. + /// + public virtual Quaternion Rotation + { + get => Transform.rotation; + set => Transform.rotation = value; + } + + /// + /// Gets the nearest game entity to the specified within the given distance. + /// + /// The to compare. + /// The maximum distance the game entity can be from the to be included. + /// + /// The nearest within the specified distance, or if no game + /// entity is found. + /// + public static GameEntity GetNearestEntity(Vector3 vector, float distance) => GetNearestEntities(vector, distance).OrderBy(p => (vector - p.GameObject.transform.position).sqrMagnitude).FirstOrDefault(); + + /// + /// Gets all game entities near the specified within the given distance. + /// + /// The to compare. + /// The maximum distance the game entity can be from the to be included. + /// The filtered collection of objects. + public static IEnumerable GetNearestEntities(Vector3 vector, float distance) => List.Where(p => p.GameObject.transform && (vector - p.GameObject.transform.position).sqrMagnitude <= distance * distance); + + /// + /// Gets the farthest game entity from the specified within the given distance. + /// + /// The to compare. + /// The minimum distance the game entity can be from the to be included. + /// + /// The farthest from the specified within the given + /// distance, or if no game entity is found. + /// + public static GameEntity GetFarthestEntity(Vector3 vector, float distance) => GetFarthestEntities(vector, distance).OrderByDescending(p => (vector - p.GameObject.transform.position).sqrMagnitude).FirstOrDefault(); + + /// + /// Gets all game entities that have a distance greater than the specified distance from the given . + /// + /// The to compare. + /// The minimum distance the game entity can be from the to be included. + /// The filtered collection of objects. + public static IEnumerable GetFarthestEntities(Vector3 vector, float distance) => List.Where(p => p.GameObject.transform && (vector - p.GameObject.transform.position).sqrMagnitude >= distance * distance); + /// public T AddComponent(string name = "") where T : EActor @@ -129,6 +216,147 @@ public IEnumerable AddComponents(IEnumerable types) yield return AddComponent(type); } + /// + public T RemoveComponent(string name = "") + where T : EActor + { + T comp = null; + + if (string.IsNullOrEmpty(name)) + { + if (!TryGetComponent(out comp)) + return null; + + comp.Base = null; + componentsInChildren.Remove(comp); + return comp; + } + + foreach (EActor actor in GetComponents()) + { + if (actor.Name != name) + continue; + + comp = actor.Cast(); + } + + return comp; + } + + /// + public T RemoveComponent(EActor actor, string name = "") + where T : EActor + { + T comp = null; + + if (string.IsNullOrEmpty(name)) + { + if (!TryGetComponent(out comp) || comp != actor) + return null; + + comp.Base = null; + componentsInChildren.Remove(comp); + return comp; + } + + foreach (EActor component in GetComponents()) + { + if (component.Name != name && component == actor) + continue; + + comp = component.Cast(); + } + + return comp; + } + + /// + public EActor RemoveComponent(Type type, string name = "") + { + EActor comp = null; + + if (string.IsNullOrEmpty(name)) + { + if (!TryGetComponent(type, out comp)) + return null; + + comp.Base = null; + componentsInChildren.Remove(comp); + return comp; + } + + foreach (EActor actor in GetComponents(type)) + { + if (actor.Name != name) + continue; + + comp = actor; + } + + return comp; + } + + /// + public EActor RemoveComponent(EActor actor, string name = "") + { + if (!componentsInChildren.Contains(actor)) + return null; + + if (string.IsNullOrEmpty(name)) + { + actor.Base = null; + componentsInChildren.Remove(actor); + return actor; + } + + foreach (EActor component in componentsInChildren) + { + if (component != actor || actor.Name != name) + continue; + + actor = component; + } + + return actor; + } + + /// + public IEnumerable RemoveComponentOfType(string name = "") + where T : EActor + { + IEnumerable components = GetComponents(); + + foreach (T comp in components) + RemoveComponent(comp, name); + + return components; + } + + /// + public IEnumerable RemoveComponentOfType(Type type, string name = "") + { + IEnumerable components = GetComponents(type); + + foreach (EActor comp in components) + RemoveComponent(comp, name); + + return components; + } + + /// + public void RemoveComponents(IEnumerable types) => types.ForEach(type => RemoveComponent(type)); + + /// + public void RemoveComponents(IEnumerable actors) => actors.ForEach(actor => RemoveComponent(actor)); + + /// + public void RemoveComponents(IEnumerable actors) + where T : EActor => actors.ForEach(actor => RemoveComponent(actor)); + + /// + public void RemoveComponents(IEnumerable types) + where T : EActor => types.ForEach(type => RemoveComponent(type)); + /// public T GetComponent() where T : EActor => componentsInChildren.FirstOrDefault(comp => typeof(T) == comp.GetType()).Cast(); diff --git a/Exiled.API/Features/Core/Generics/EBehaviour.cs b/Exiled.API/Features/Core/Generic/EBehaviour.cs similarity index 98% rename from Exiled.API/Features/Core/Generics/EBehaviour.cs rename to Exiled.API/Features/Core/Generic/EBehaviour.cs index 9465ccc711..bc827be49f 100644 --- a/Exiled.API/Features/Core/Generics/EBehaviour.cs +++ b/Exiled.API/Features/Core/Generic/EBehaviour.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using Exiled.API.Features.Core; diff --git a/Exiled.API/Features/Core/Generics/EnumClass.cs b/Exiled.API/Features/Core/Generic/EnumClass.cs similarity index 99% rename from Exiled.API/Features/Core/Generics/EnumClass.cs rename to Exiled.API/Features/Core/Generic/EnumClass.cs index 1c95b630b7..cf9413f66a 100644 --- a/Exiled.API/Features/Core/Generics/EnumClass.cs +++ b/Exiled.API/Features/Core/Generic/EnumClass.cs @@ -5,14 +5,14 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using LiteNetLib.Utils; diff --git a/Exiled.API/Features/Pools/DictionaryPool.cs b/Exiled.API/Features/Core/Generic/Pools/DictionaryPool.cs similarity index 86% rename from Exiled.API/Features/Pools/DictionaryPool.cs rename to Exiled.API/Features/Core/Generic/Pools/DictionaryPool.cs index 4b5bcbe80a..aa6919b302 100644 --- a/Exiled.API/Features/Pools/DictionaryPool.cs +++ b/Exiled.API/Features/Core/Generic/Pools/DictionaryPool.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Pools +namespace Exiled.API.Features.Core.Generic.Pools { using System.Collections.Concurrent; using System.Collections.Generic; @@ -37,10 +37,17 @@ private DictionaryPool() /// The . public Dictionary Get() { - /*if (pool.TryDequeue(out Dictionary result)) - return result;*/ + if (pool.TryDequeue(out Dictionary result)) + { + pool.Enqueue(new Dictionary()); + result.Clear(); + } + else + { + result = new Dictionary(); + } - return new(); + return result; } /// @@ -50,8 +57,7 @@ public Dictionary Get() /// The . public Dictionary Get(IEnumerable> pairs) { - // if (!pool.TryDequeue(out Dictionary dict)) - Dictionary dict = new(); + Dictionary dict = Get(); foreach (KeyValuePair pair in pairs) dict.Add(pair.Key, pair.Value); @@ -65,8 +71,8 @@ public Dictionary Get(IEnumerable> pair /// The to return. public void Return(Dictionary obj) { - // obj.Clear(); - // pool.Enqueue(obj); + obj.Clear(); + pool.Enqueue(obj); } /// @@ -77,9 +83,7 @@ public void Return(Dictionary obj) public KeyValuePair[] ToArrayReturn(Dictionary obj) { KeyValuePair[] array = obj.ToArray(); - Return(obj); - return array; } } diff --git a/Exiled.API/Features/Pools/HashSetPool.cs b/Exiled.API/Features/Core/Generic/Pools/HashSetPool.cs similarity index 97% rename from Exiled.API/Features/Pools/HashSetPool.cs rename to Exiled.API/Features/Core/Generic/Pools/HashSetPool.cs index 605e44ddc4..fe9b9cbd38 100644 --- a/Exiled.API/Features/Pools/HashSetPool.cs +++ b/Exiled.API/Features/Core/Generic/Pools/HashSetPool.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Pools +namespace Exiled.API.Features.Core.Generic.Pools { using System.Collections.Generic; using System.Linq; @@ -50,9 +50,7 @@ private HashSetPool() public T[] ToArrayReturn(HashSet obj) { T[] array = obj.ToArray(); - Return(obj); - return array; } } diff --git a/Exiled.API/Features/Pools/IPool.cs b/Exiled.API/Features/Core/Generic/Pools/IPool.cs similarity index 95% rename from Exiled.API/Features/Pools/IPool.cs rename to Exiled.API/Features/Core/Generic/Pools/IPool.cs index 8449ab5ab8..40eac33b4b 100644 --- a/Exiled.API/Features/Pools/IPool.cs +++ b/Exiled.API/Features/Core/Generic/Pools/IPool.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Pools +namespace Exiled.API.Features.Core.Generic.Pools { /// /// Defines the contract for a class that stores and retrieves commonly used objects. diff --git a/Exiled.API/Features/Pools/ListPool.cs b/Exiled.API/Features/Core/Generic/Pools/ListPool.cs similarity index 98% rename from Exiled.API/Features/Pools/ListPool.cs rename to Exiled.API/Features/Core/Generic/Pools/ListPool.cs index ce68e670b5..537c953f31 100644 --- a/Exiled.API/Features/Pools/ListPool.cs +++ b/Exiled.API/Features/Core/Generic/Pools/ListPool.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Pools +namespace Exiled.API.Features.Core.Generic.Pools { using System.Collections.Generic; @@ -56,9 +56,7 @@ private ListPool() public T[] ToArrayReturn(List obj) { T[] array = obj.ToArray(); - Return(obj); - return array; } } diff --git a/Exiled.API/Features/Pools/QueuePool.cs b/Exiled.API/Features/Core/Generic/Pools/QueuePool.cs similarity index 98% rename from Exiled.API/Features/Pools/QueuePool.cs rename to Exiled.API/Features/Core/Generic/Pools/QueuePool.cs index 6b2afae64b..a4d7fe1cb2 100644 --- a/Exiled.API/Features/Pools/QueuePool.cs +++ b/Exiled.API/Features/Core/Generic/Pools/QueuePool.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Pools +namespace Exiled.API.Features.Core.Generic.Pools { using System.Collections.Concurrent; using System.Collections.Generic; @@ -71,9 +71,7 @@ public void Return(Queue obj) public T[] ToArrayReturn(Queue obj) { T[] array = obj.ToArray(); - Return(obj); - return array; } } diff --git a/Exiled.API/Features/Pools/StringBuilderPool.cs b/Exiled.API/Features/Core/Generic/Pools/StringBuilderPool.cs similarity index 97% rename from Exiled.API/Features/Pools/StringBuilderPool.cs rename to Exiled.API/Features/Core/Generic/Pools/StringBuilderPool.cs index d36a70b343..35cb20a828 100644 --- a/Exiled.API/Features/Pools/StringBuilderPool.cs +++ b/Exiled.API/Features/Core/Generic/Pools/StringBuilderPool.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Pools +namespace Exiled.API.Features.Core.Generic.Pools { using System.Text; @@ -46,9 +46,7 @@ private StringBuilderPool() public string ToStringReturn(StringBuilder obj) { string s = obj.ToString(); - Return(obj); - return s; } } diff --git a/Exiled.API/Features/Core/Generics/RepNotify.cs b/Exiled.API/Features/Core/Generic/RepNotify.cs similarity index 98% rename from Exiled.API/Features/Core/Generics/RepNotify.cs rename to Exiled.API/Features/Core/Generic/RepNotify.cs index f59bf8fc71..c90b64e15d 100644 --- a/Exiled.API/Features/Core/Generics/RepNotify.cs +++ b/Exiled.API/Features/Core/Generic/RepNotify.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using System.Reflection; diff --git a/Exiled.API/Features/Core/Generics/ReplicatedProperty.cs b/Exiled.API/Features/Core/Generic/ReplicatedProperty.cs similarity index 98% rename from Exiled.API/Features/Core/Generics/ReplicatedProperty.cs rename to Exiled.API/Features/Core/Generic/ReplicatedProperty.cs index bc584c1921..8dcbe742b7 100644 --- a/Exiled.API/Features/Core/Generics/ReplicatedProperty.cs +++ b/Exiled.API/Features/Core/Generic/ReplicatedProperty.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using System; diff --git a/Exiled.API/Features/Core/Generics/ReplicatedRef.cs b/Exiled.API/Features/Core/Generic/ReplicatedRef.cs similarity index 98% rename from Exiled.API/Features/Core/Generics/ReplicatedRef.cs rename to Exiled.API/Features/Core/Generic/ReplicatedRef.cs index 44c1d90dca..0e58d86389 100644 --- a/Exiled.API/Features/Core/Generics/ReplicatedRef.cs +++ b/Exiled.API/Features/Core/Generic/ReplicatedRef.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using Exiled.API.Features.Core.Attributes; using Exiled.API.Features.Core.EventArgs; diff --git a/Exiled.API/Features/Core/Generics/Singleton.cs b/Exiled.API/Features/Core/Generic/Singleton.cs similarity index 98% rename from Exiled.API/Features/Core/Generics/Singleton.cs rename to Exiled.API/Features/Core/Generic/Singleton.cs index 945cb90356..9b4809e653 100644 --- a/Exiled.API/Features/Core/Generics/Singleton.cs +++ b/Exiled.API/Features/Core/Generic/Singleton.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using System.Collections.Generic; using System.Linq; diff --git a/Exiled.API/Features/Core/Generics/StaticActor.cs b/Exiled.API/Features/Core/Generic/StaticActor.cs similarity index 97% rename from Exiled.API/Features/Core/Generics/StaticActor.cs rename to Exiled.API/Features/Core/Generic/StaticActor.cs index 62bbbd39b2..917658b1e3 100644 --- a/Exiled.API/Features/Core/Generics/StaticActor.cs +++ b/Exiled.API/Features/Core/Generic/StaticActor.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using Exiled.API.Features; using Exiled.API.Features.Core; @@ -61,6 +61,10 @@ public static T CreateNewInstance() { EObject @object = CreateDefaultSubobject(); @object.Name = "__" + typeof(T).Name + " (StaticActor)"; + + if (Server.Host.GameObject) + @object.Base = Server.Host.GameObject; + return @object.Cast(); } diff --git a/Exiled.API/Features/Core/Generics/UniqueUnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs similarity index 99% rename from Exiled.API/Features/Core/Generics/UniqueUnmanagedEnumClass.cs rename to Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs index 58ca57b50e..94a91cac48 100644 --- a/Exiled.API/Features/Core/Generics/UniqueUnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UniqueUnmanagedEnumClass.cs @@ -5,14 +5,14 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using LiteNetLib.Utils; diff --git a/Exiled.API/Features/Core/Generics/UnmanagedEnumClass.cs b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs similarity index 99% rename from Exiled.API/Features/Core/Generics/UnmanagedEnumClass.cs rename to Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs index d751d0bb9b..63308fa4e1 100644 --- a/Exiled.API/Features/Core/Generics/UnmanagedEnumClass.cs +++ b/Exiled.API/Features/Core/Generic/UnmanagedEnumClass.cs @@ -5,14 +5,14 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.Core.Generics +namespace Exiled.API.Features.Core.Generic { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using LiteNetLib.Utils; diff --git a/Exiled.API/Features/Core/Interfaces/IAdditiveSettings.cs b/Exiled.API/Features/Core/Interfaces/IAdditiveSettings.cs index 6b8726ca65..6213484f39 100644 --- a/Exiled.API/Features/Core/Interfaces/IAdditiveSettings.cs +++ b/Exiled.API/Features/Core/Interfaces/IAdditiveSettings.cs @@ -7,6 +7,8 @@ namespace Exiled.API.Features.Core.Interfaces { + using Exiled.API.Features.Core.Behaviours; + /// /// Defines additive settings set up through user-defined properties. /// diff --git a/Exiled.API/Features/Core/Interfaces/IAdditiveSettingsCollection.cs b/Exiled.API/Features/Core/Interfaces/IAdditiveSettingsCollection.cs index 0f778f4bd9..28314c9089 100644 --- a/Exiled.API/Features/Core/Interfaces/IAdditiveSettingsCollection.cs +++ b/Exiled.API/Features/Core/Interfaces/IAdditiveSettingsCollection.cs @@ -9,6 +9,8 @@ namespace Exiled.API.Features.Core.Interfaces { using System.Collections.Generic; + using Exiled.API.Features.Core.Behaviours; + /// /// Defines a collection of additive settings set up through user-defined properties. /// diff --git a/Exiled.API/Features/Core/Interfaces/IEntity.cs b/Exiled.API/Features/Core/Interfaces/IEntity.cs index 0b3e7beedc..aee4dadee8 100644 --- a/Exiled.API/Features/Core/Interfaces/IEntity.cs +++ b/Exiled.API/Features/Core/Interfaces/IEntity.cs @@ -97,6 +97,86 @@ public abstract IEnumerable AddComponents(IEnumerable actors) public abstract IEnumerable AddComponents(IEnumerable types) where T : EActor; + /// + /// Removes a component from the . + /// + /// The to be removed. + /// The name of the component. + /// The removed component. + public abstract T RemoveComponent(string name = "") + where T : EActor; + + /// + /// Removes a component from the . + /// + /// The cast type. + /// The to be removed. + /// The name of the component. + /// The removed component. + public abstract T RemoveComponent(EActor actor, string name = "") + where T : EActor; + + /// + /// Removes a component from the . + /// + /// The of the to be removed. + /// The name of the component. + /// The removed component. + public abstract EActor RemoveComponent(Type type, string name = ""); + + /// + /// Removes a component from the . + /// + /// The to be removed. + /// The name of the component. + /// The removed component. + public abstract EActor RemoveComponent(EActor actor, string name = ""); + + /// + /// Removes all components of the specified type from the . + /// + /// The type to be removed. + /// The name of the component. + /// The removed components. + public abstract IEnumerable RemoveComponentOfType(string name = "") + where T : EActor; + + /// + /// Removes all components of the specified type from the . + /// + /// The of the to be removed. + /// The name of the component. + /// The removed components. + public abstract IEnumerable RemoveComponentOfType(Type type, string name = ""); + + /// + /// Removes multiple components from the . + /// + /// The collection of representing the components to be removed. + public abstract void RemoveComponents(IEnumerable types); + + /// + /// Removes multiple components from the . + /// + /// The collection of instances to be removed. + public abstract void RemoveComponents(IEnumerable actors); + + /// + /// Removes multiple components from the . + /// + /// The type to be removed. + /// The collection of instances to be removed. + public abstract void RemoveComponents(IEnumerable actors) + where T : EActor; + + /// + /// Removes multiple components from the . + /// + /// The type to be removed. + /// The collection of representing the components to be removed. + public abstract void RemoveComponents(IEnumerable types) + where T : EActor; + /// /// Gets a component from the . /// diff --git a/Exiled.API/Features/Core/StaticActor.cs b/Exiled.API/Features/Core/StaticActor.cs index b47b48841d..93860a2ef5 100644 --- a/Exiled.API/Features/Core/StaticActor.cs +++ b/Exiled.API/Features/Core/StaticActor.cs @@ -56,6 +56,10 @@ public static StaticActor CreateNewInstance(Type type) { EObject @object = CreateDefaultSubobject(type); @object.Name = "__" + type.Name + " (StaticActor)"; + + if (Server.Host.GameObject) + @object.Base = Server.Host.GameObject; + return @object.Cast(); } diff --git a/Exiled.API/Features/DamageHandlers/AttackerDamageHandler.cs b/Exiled.API/Features/DamageHandlers/AttackerDamageHandler.cs index 0750c9d5be..b1e9ef4d67 100644 --- a/Exiled.API/Features/DamageHandlers/AttackerDamageHandler.cs +++ b/Exiled.API/Features/DamageHandlers/AttackerDamageHandler.cs @@ -41,10 +41,10 @@ protected AttackerDamageHandler(Player target, BaseHandler baseHandler) /// public bool ForceFullFriendlyFire { - get => Is(out PlayerStatsSystem.AttackerDamageHandler handler) && handler.ForceFullFriendlyFire; + get => Base is PlayerStatsSystem.AttackerDamageHandler handler && handler.ForceFullFriendlyFire; set { - if (Is(out PlayerStatsSystem.AttackerDamageHandler handler)) + if (Base is PlayerStatsSystem.AttackerDamageHandler handler) handler.ForceFullFriendlyFire = value; } } @@ -54,10 +54,10 @@ public bool ForceFullFriendlyFire /// public bool IsSuicide { - get => Is(out PlayerStatsSystem.AttackerDamageHandler handler) && handler.IsSuicide; + get => Base is PlayerStatsSystem.AttackerDamageHandler handler && handler.IsSuicide; set { - if (Is(out PlayerStatsSystem.AttackerDamageHandler handler)) + if (Base is PlayerStatsSystem.AttackerDamageHandler handler) handler.IsSuicide = value; } } @@ -65,17 +65,17 @@ public bool IsSuicide /// /// Gets a value indicating whether the self damage is allowed. /// - public bool AllowSelfDamage => Is(out PlayerStatsSystem.AttackerDamageHandler handler) && handler.AllowSelfDamage; + public bool AllowSelfDamage => Base is PlayerStatsSystem.AttackerDamageHandler handler && handler.AllowSelfDamage; /// /// Gets or sets a value indicating whether the damage is friendly fire. /// public bool IsFriendlyFire { - get => Is(out PlayerStatsSystem.AttackerDamageHandler handler) && handler.IsFriendlyFire; + get => Base is PlayerStatsSystem.AttackerDamageHandler handler && handler.IsFriendlyFire; set { - if (Is(out PlayerStatsSystem.AttackerDamageHandler handler)) + if (Base is PlayerStatsSystem.AttackerDamageHandler handler) handler.IsFriendlyFire = value; } } @@ -86,12 +86,11 @@ public bool IsFriendlyFire /// The to damage. public override void ProcessDamage(Player player) { - if (!Is(out PlayerStatsSystem.AttackerDamageHandler _)) + if (Base is not PlayerStatsSystem.AttackerDamageHandler) return; if ((player.IsSpawnProtected && (player != Attacker)) || - (!SpawnProtected.PreventAllDamage && - Attacker is not null && Attacker.IsSpawnProtected)) + (!SpawnProtected.PreventAllDamage && Attacker is not null && Attacker.IsSpawnProtected)) { Damage = 0f; return; @@ -99,7 +98,7 @@ public override void ProcessDamage(Player player) if ((player != Attacker) && !ForceFullFriendlyFire) { - if (HitboxIdentity.CheckFriendlyFire(AttackerFootprint.Role, player.Role, true)) + if (HitboxIdentity.IsEnemy(AttackerFootprint.Role, player.Role)) return; Damage *= PlayerStatsSystem.AttackerDamageHandler._ffMultiplier; diff --git a/Exiled.API/Features/DamageHandlers/CustomDamageHandler.cs b/Exiled.API/Features/DamageHandlers/CustomDamageHandler.cs index 47aa8f4f13..42533031f0 100644 --- a/Exiled.API/Features/DamageHandlers/CustomDamageHandler.cs +++ b/Exiled.API/Features/DamageHandlers/CustomDamageHandler.cs @@ -7,16 +7,13 @@ namespace Exiled.API.Features.DamageHandlers { - using CustomPlayerEffects; + using System.Collections.Generic; + using CustomPlayerEffects; using Enums; - using Exiled.API.Extensions; - using Items; - using PlayerStatsSystem; - using UnityEngine; using BaseFirearmHandler = PlayerStatsSystem.FirearmDamageHandler; @@ -28,6 +25,22 @@ namespace Exiled.API.Features.DamageHandlers /// public sealed class CustomDamageHandler : AttackerDamageHandler { + private static readonly Dictionary ItemTypeToDamage = new() + { + [ItemType.GunCrossvec] = DamageType.Crossvec, + [ItemType.GunLogicer] = DamageType.Logicer, + [ItemType.GunRevolver] = DamageType.Revolver, + [ItemType.GunShotgun] = DamageType.Shotgun, + [ItemType.GunCOM15] = DamageType.Com15, + [ItemType.GunCOM18] = DamageType.Com18, + [ItemType.GunFSP9] = DamageType.Fsp9, + [ItemType.GunE11SR] = DamageType.E11Sr, + [ItemType.GunCom45] = DamageType.Com45, + [ItemType.GunFRMG0] = DamageType.Frmg0, + [ItemType.GunA7] = DamageType.A7, + [ItemType.GunAK] = DamageType.AK, + }; + /// /// Initializes a new instance of the class. /// @@ -36,7 +49,7 @@ public sealed class CustomDamageHandler : AttackerDamageHandler public CustomDamageHandler(Player target, BaseHandler baseHandler) : base(target, baseHandler) { - if (Attacker is not null) + if (Attacker) { if (baseHandler is BaseScpDamageHandler) CustomBase = new ScpDamageHandler(target, baseHandler); @@ -74,6 +87,25 @@ public CustomDamageHandler(Player target, Player attacker, float damage, DamageT CustomBase = new FirearmDamageHandler(firearm, target, new BaseFirearmHandler(firearm.Base, damage)); } + /// + /// Initializes a new instance of the class. + /// + /// The target to be set. + /// The attacker to be set. + /// The amount of damage to be set. + /// The to be used. + public CustomDamageHandler(Player target, Player attacker, float damage, Firearm firearm) + : base(target, attacker) + { + Damage = damage; + Type = ItemTypeToDamage[firearm.Type]; + + if (firearm.Owner != attacker) + firearm.ChangeOwner(firearm.Owner, attacker); + + CustomBase = new FirearmDamageHandler(firearm, target, new BaseFirearmHandler(firearm.Base, damage)); + } + /// /// Initializes a new instance of the class. /// @@ -83,10 +115,7 @@ public CustomDamageHandler(Player target, Player attacker, float damage, DamageT /// The to be set. /// The to be set. public CustomDamageHandler(Player target, Player attacker, float damage, DamageType damageType, CassieAnnouncement cassieAnnouncement) - : this(target, attacker, damage, damageType) - { - CassieDeathAnnouncement = cassieAnnouncement; - } + : this(target, attacker, damage, damageType) => CassieDeathAnnouncement = cassieAnnouncement; /// /// Initializes a new instance of the class. @@ -97,10 +126,7 @@ public CustomDamageHandler(Player target, Player attacker, float damage, DamageT /// The to be set. /// The to be set. public CustomDamageHandler(Player target, Player attacker, float damage, DamageType damageType, string cassieAnnouncement) - : this(target, attacker, damage, damageType) - { - CassieDeathAnnouncement = new CassieAnnouncement(cassieAnnouncement); - } + : this(target, attacker, damage, damageType) => CassieDeathAnnouncement = new CassieAnnouncement(cassieAnnouncement); /// /// Gets the base . @@ -115,7 +141,14 @@ public override Action ApplyDamage(Player player) StartVelocity = player.Velocity; - As().StartVelocity.y = Mathf.Max(As().StartVelocity.y, 0f); + BaseFirearmHandler baseFirearmHandler = null; + + if (Base is BaseFirearmHandler) + baseFirearmHandler = Base as BaseFirearmHandler; + + if (baseFirearmHandler is not null) + baseFirearmHandler.StartVelocity.y = Mathf.Max(baseFirearmHandler.StartVelocity.y, 0f); + AhpStat ahpModule = player.GetModule(); HealthStat healthModule = player.GetModule(); @@ -124,10 +157,13 @@ public override Action ApplyDamage(Player player) ProcessDamage(player); - foreach (StatusEffectBase statusEffect in player.ActiveEffects) + if (baseFirearmHandler is not null) { - if (statusEffect is IDamageModifierEffect damageModifierEffect) - Damage *= damageModifierEffect.GetDamageModifier(Damage, CustomBase, As().Hitbox); + foreach (StatusEffectBase statusEffect in player.ActiveEffects) + { + if (statusEffect is IDamageModifierEffect damageModifierEffect) + Damage *= damageModifierEffect.GetDamageModifier(Damage, CustomBase, baseFirearmHandler.Hitbox); + } } DealtHealthDamage = ahpModule.ServerProcessDamage(Damage); diff --git a/Exiled.API/Features/DamageHandlers/DamageHandler.cs b/Exiled.API/Features/DamageHandlers/DamageHandler.cs index 41be3a34bd..4cb7158b3c 100644 --- a/Exiled.API/Features/DamageHandlers/DamageHandler.cs +++ b/Exiled.API/Features/DamageHandlers/DamageHandler.cs @@ -80,10 +80,10 @@ public DamageHandler(Player target, BaseHandler baseHandler) /// public virtual float Damage { - get => Is(out StandardDamageHandler handler) ? handler.Damage : 0f; + get => Base is StandardDamageHandler handler ? handler.Damage : 0f; set { - if (Is(out StandardDamageHandler handler)) + if (Base is StandardDamageHandler handler) handler.Damage = value; } } @@ -93,10 +93,10 @@ public virtual float Damage /// public Vector3 StartVelocity { - get => Is(out StandardDamageHandler handler) ? handler.StartVelocity : Vector3.zero; + get => Base is StandardDamageHandler handler ? handler.StartVelocity : Vector3.zero; set { - if (Is(out StandardDamageHandler handler)) + if (Base is StandardDamageHandler handler) handler.StartVelocity = value; } } @@ -106,10 +106,10 @@ public Vector3 StartVelocity /// public float DealtHealthDamage { - get => Is(out StandardDamageHandler handler) ? handler.DealtHealthDamage : 0f; + get => Base is StandardDamageHandler handler ? handler.DealtHealthDamage : 0f; set { - if (Is(out StandardDamageHandler handler)) + if (Base is StandardDamageHandler handler) handler.DealtHealthDamage = value; } } @@ -119,10 +119,10 @@ public float DealtHealthDamage /// public float AbsorbedAhpDamage { - get => Is(out StandardDamageHandler handler) ? handler.AbsorbedAhpDamage : 0f; + get => Base is StandardDamageHandler handler ? handler.AbsorbedAhpDamage : 0f; set { - if (Is(out StandardDamageHandler handler)) + if (Base is StandardDamageHandler handler) handler.AbsorbedAhpDamage = value; } } @@ -130,16 +130,16 @@ public float AbsorbedAhpDamage /// public override Action ApplyDamage(Player player) { - if (!Is(out StandardDamageHandler damageHandler)) + if (Base is not StandardDamageHandler handler) return player.GetModule().CurValue > 0f ? Action.Damage : Action.Death; if (Damage <= 0f) return Action.None; - damageHandler.ApplyDamage(player.ReferenceHub); + handler.ApplyDamage(player.ReferenceHub); StartVelocity = player.Velocity; - As().StartVelocity.y = Mathf.Max(damageHandler.StartVelocity.y, 0f); + handler.StartVelocity.y = Mathf.Max(handler.StartVelocity.y, 0f); AhpStat ahpModule = player.GetModule(); HealthStat healthModule = player.GetModule(); @@ -155,7 +155,7 @@ public override Action ApplyDamage(Player player) foreach (StatusEffectBase effect in player.ActiveEffects) { if (effect is IDamageModifierEffect damageModifierEffect) - Damage *= damageModifierEffect.GetDamageModifier(Damage, damageHandler, damageHandler.Hitbox); + Damage *= damageModifierEffect.GetDamageModifier(Damage, handler, handler.Hitbox); } // DealtHealthDamage = ahpModule.ServerProcessDamage(Damage); diff --git a/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs b/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs index c4eed3f055..55db916ed0 100644 --- a/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs +++ b/Exiled.API/Features/DamageHandlers/DamageHandlerBase.cs @@ -13,6 +13,7 @@ namespace Exiled.API.Features.DamageHandlers using System.Linq; using Enums; + using Exiled.API.Features.Core; using Extensions; using PlayerRoles.PlayableScps.Scp3114; @@ -25,7 +26,7 @@ namespace Exiled.API.Features.DamageHandlers /// /// A wrapper to easily manipulate the behavior of . /// - public abstract class DamageHandlerBase + public abstract class DamageHandlerBase : TypeCastObject { private DamageType damageType; private CassieAnnouncement cassieAnnouncement; @@ -96,55 +97,45 @@ public virtual DamageType Type if (damageType != DamageType.Unknown) return damageType; - switch (Base) + if (Base is UniversalDamageHandler handler) { - case CustomReasonDamageHandler: - return DamageType.Custom; - case WarheadDamageHandler: - return DamageType.Warhead; - case ExplosionDamageHandler: - return DamageType.Explosion; - case Scp018DamageHandler: - return DamageType.Scp018; - case RecontainmentDamageHandler: - return DamageType.Recontainment; - case MicroHidDamageHandler: - return DamageType.MicroHid; - case DisruptorDamageHandler: - return DamageType.ParticleDisruptor; - case Scp939DamageHandler: - return DamageType.Scp939; - case JailbirdDamageHandler: - return DamageType.Jailbird; - case Scp3114DamageHandler scp3114DamageHandler: - return scp3114DamageHandler.Subtype switch - { - Scp3114DamageHandler.HandlerType.Strangulation => DamageType.Strangled, - Scp3114DamageHandler.HandlerType.SkinSteal => DamageType.Scp3114, - Scp3114DamageHandler.HandlerType.Slap => DamageType.Scp3114, - _ => DamageType.Unknown, - }; - case Scp049DamageHandler scp049DamageHandler: - return scp049DamageHandler.DamageSubType switch - { - Scp049DamageHandler.AttackType.CardiacArrest => DamageType.CardiacArrest, - Scp049DamageHandler.AttackType.Instakill => DamageType.Scp049, - Scp049DamageHandler.AttackType.Scp0492 => DamageType.Scp0492, - _ => DamageType.Unknown, - }; - case UniversalDamageHandler universal: - { - DeathTranslation translation = DeathTranslations.TranslationsById[universal.TranslationId]; - - if (DamageTypeExtensions.TranslationIdConversion.ContainsKey(translation.Id)) - return DamageTypeExtensions.TranslationIdConversion[translation.Id]; - - Log.Warn($"{nameof(DamageHandler)}.{nameof(Type)}: No matching {nameof(DamageType)} for {nameof(UniversalDamageHandler)} with ID {translation.Id}, type will be reported as {DamageType.Unknown}. Report this to EXILED Devs."); - break; - } + DeathTranslation translation = DeathTranslations.TranslationsById[handler.TranslationId]; + + if (DamageTypeExtensions.TranslationIdConversion.ContainsKey(translation.Id)) + return damageType = DamageTypeExtensions.TranslationIdConversion[translation.Id]; + + Log.Warn($"{nameof(DamageHandler)}.{nameof(Type)}:" + + $"No matching {nameof(DamageType)} for {nameof(UniversalDamageHandler)} with ID {translation.Id}, type will be reported as {DamageType.Unknown}." + + $"Report this to EXILED Devs."); } - return DamageType.Unknown; + return damageType = Base switch + { + CustomReasonDamageHandler => DamageType.Custom, + WarheadDamageHandler => DamageType.Warhead, + ExplosionDamageHandler => DamageType.Explosion, + Scp018DamageHandler => DamageType.Scp018, + RecontainmentDamageHandler => DamageType.Recontainment, + MicroHidDamageHandler => DamageType.MicroHid, + DisruptorDamageHandler => DamageType.ParticleDisruptor, + Scp939DamageHandler => DamageType.Scp939, + JailbirdDamageHandler => DamageType.Jailbird, + Scp3114DamageHandler scp3114DamageHandler => scp3114DamageHandler.Subtype switch + { + Scp3114DamageHandler.HandlerType.Strangulation => DamageType.Strangled, + Scp3114DamageHandler.HandlerType.SkinSteal => DamageType.Scp3114, + Scp3114DamageHandler.HandlerType.Slap => DamageType.Scp3114, + _ => DamageType.Unknown, + }, + Scp049DamageHandler scp049DamageHandler => scp049DamageHandler.DamageSubType switch + { + Scp049DamageHandler.AttackType.CardiacArrest => DamageType.CardiacArrest, + Scp049DamageHandler.AttackType.Instakill => DamageType.Scp049, + Scp049DamageHandler.AttackType.Scp0492 => DamageType.Scp0492, + _ => DamageType.Unknown, + }, + _ => throw new InvalidOperationException("Unknown damage type."), + }; } protected set @@ -182,58 +173,6 @@ public virtual void ProcessDamage(Player player) { } - /// - /// Unsafely casts the damage handler to the specified type. - /// - /// The specified type. - /// A object. - public T As() - where T : BaseHandler => Base as T; - - /// - /// Unsafely casts the damage handler to the specified type. - /// - /// The specified type. - /// A object. - public T BaseAs() - where T : DamageHandlerBase => this as T; - - /// - /// Safely casts the damage handler to the specified type. - /// - /// The specified type. - /// The casted . - /// A object. - public bool Is(out T param) - where T : BaseHandler - { - param = default; - - if (Base is not T cast) - return false; - - param = cast; - return true; - } - - /// - /// Safely casts the damage handler to the specified type. - /// - /// The specified type. - /// The casted . - /// A object. - public bool BaseIs(out T param) - where T : DamageHandlerBase - { - param = default; - - if (this is not T cast) - return false; - - param = cast; - return true; - } - /// /// A wrapper to easily manipulate the behavior of . /// diff --git a/Exiled.API/Features/DamageHandlers/FirearmDamageHandler.cs b/Exiled.API/Features/DamageHandlers/FirearmDamageHandler.cs index bef0599005..e404cef570 100644 --- a/Exiled.API/Features/DamageHandlers/FirearmDamageHandler.cs +++ b/Exiled.API/Features/DamageHandlers/FirearmDamageHandler.cs @@ -53,27 +53,32 @@ Firearm _ when DamageTypeExtensions.ItemConversion.ContainsKey(Item.Type) => Dam /// public HitboxType Hitbox { - get => As().Hitbox; - set => As().Hitbox = value; + get => (Base as BaseFirearmHandler).Hitbox; + set => (Base as BaseFirearmHandler).Hitbox = value; } /// /// Gets the penetration. /// - public float Penetration => As()._penetration; + public float Penetration => (Base as BaseFirearmHandler)._penetration; /// /// Gets a value indicating whether the human hitboxes should be used. /// - public bool UseHumanHitboxes => As()._useHumanHitboxes; + public bool UseHumanHitboxes => (Base as BaseFirearmHandler)._useHumanHitboxes; /// public override void ProcessDamage(Player player) { - if (Is(out BaseFirearmHandler firearmHandler)) - firearmHandler.ProcessDamage(player.ReferenceHub); - else if (Is(out MicroHidDamageHandler microHidHandler)) - microHidHandler.ProcessDamage(player.ReferenceHub); + switch (Base) + { + case BaseFirearmHandler firearmDamageHandler: + firearmDamageHandler.ProcessDamage(player.ReferenceHub); + break; + case MicroHidDamageHandler microHidHandler: + microHidHandler.ProcessDamage(player.ReferenceHub); + break; + } } /// diff --git a/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs b/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs index 56c6ad2822..5bd7a967a1 100644 --- a/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs +++ b/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs @@ -7,17 +7,19 @@ namespace Exiled.API.Features.DamageHandlers { - using Enums; + using System.Collections.Generic; + using Enums; + using Exiled.API.Extensions; + using Exiled.API.Features.Pickups; using Footprinting; - + using InventorySystem.Items.MicroHID; + using InventorySystem.Items.ThrowableProjectiles; using Items; - using PlayerRoles.PlayableScps.Scp096; + using PlayerRoles.PlayableScps.Scp3114; using PlayerRoles.PlayableScps.Scp939; - using PlayerStatsSystem; - using UnityEngine; /// @@ -26,6 +28,48 @@ namespace Exiled.API.Features.DamageHandlers public class GenericDamageHandler : CustomReasonDamageHandler { private const string DamageTextDefault = "You were damaged by Unknown Cause"; + + private static readonly Dictionary DamageToTranslations = new() + { + [DamageType.Falldown] = DeathTranslations.Falldown, + [DamageType.CardiacArrest] = DeathTranslations.CardiacArrest, + [DamageType.Hypothermia] = DeathTranslations.Hypothermia, + [DamageType.Asphyxiation] = DeathTranslations.Asphyxiated, + [DamageType.Poison] = DeathTranslations.Poisoned, + [DamageType.Bleeding] = DeathTranslations.Bleeding, + [DamageType.Crushed] = DeathTranslations.Crushed, + [DamageType.FemurBreaker] = DeathTranslations.UsedAs106Bait, + [DamageType.PocketDimension] = DeathTranslations.PocketDecay, + [DamageType.FriendlyFireDetector] = DeathTranslations.FriendlyFireDetector, + [DamageType.SeveredHands] = DeathTranslations.SeveredHands, + [DamageType.Decontamination] = DeathTranslations.Decontamination, + [DamageType.Tesla] = DeathTranslations.Tesla, + [DamageType.Scp] = DeathTranslations.Unknown, + [DamageType.Scp207] = DeathTranslations.Scp207, + [DamageType.Scp049] = DeathTranslations.Scp049, + [DamageType.Scp173] = DeathTranslations.Scp173, + [DamageType.Scp0492] = DeathTranslations.Zombie, + [DamageType.Scp106] = DeathTranslations.PocketDecay, + [DamageType.Scp3114] = DeathTranslations.Scp3114Slap, + }; + + private static readonly Dictionary DamageToItemType = new() + { + [DamageType.Crossvec] = ItemType.GunCrossvec, + [DamageType.Logicer] = ItemType.GunLogicer, + [DamageType.Revolver] = ItemType.GunRevolver, + [DamageType.Shotgun] = ItemType.GunShotgun, + [DamageType.Com15] = ItemType.GunCOM15, + [DamageType.Com18] = ItemType.GunCOM18, + [DamageType.Fsp9] = ItemType.GunFSP9, + [DamageType.E11Sr] = ItemType.GunE11SR, + [DamageType.Com45] = ItemType.GunCom45, + [DamageType.Frmg0] = ItemType.GunFRMG0, + [DamageType.A7] = ItemType.GunA7, + [DamageType.AK] = ItemType.GunAK, + [DamageType.Firearm] = ItemType.GunAK, + }; + private string genericDamageText; private string genericEnvironmentDamageText; private Player player; @@ -36,12 +80,12 @@ public class GenericDamageHandler : CustomReasonDamageHandler /// Initializes a new instance of the class. /// Transform input data to custom generic handler. /// - /// Current player (Target). - /// Attacker. - /// Damage quantity. - /// Damage type. - /// Custom cassie announcment. - /// Text to provide to player death screen. + /// Current player (Target). + /// Attacker. + /// Damage amount. + /// Damage type. + /// Custom cassie announcement. + /// Text to provide for player death screen. public GenericDamageHandler(Player player, Player attacker, float damage, DamageType damageType, DamageHandlerBase.CassieAnnouncement cassieAnnouncement, string damageText = null) : base(DamageTextDefault) { @@ -58,48 +102,25 @@ public GenericDamageHandler(Player player, Player attacker, float damage, Damage Damage = damage; ServerLogsText = $"GenericDamageHandler damage processing"; genericDamageText = $"You were damaged by {damageType}"; - genericEnvironmentDamageText = $"Environemntal damage of type {damageType}"; + genericEnvironmentDamageText = $"Environmental damage of type {damageType}"; + + if (DamageToTranslations.TryGetValue(damageType, out DeathTranslation translation)) + { + Base = damageType.IsScp() ? + new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, translation) : + new UniversalDamageHandler(damage, translation, cassieAnnouncement); + } + + if (Base is null && DamageToItemType.TryGetValue(damageType, out ItemType itemType)) + GenericFirearm(player, attacker, damage, damageType, itemType); switch (damageType) { - case DamageType.Falldown: - Base = new UniversalDamageHandler(damage, DeathTranslations.Falldown, cassieAnnouncement); - break; - case DamageType.Hypothermia: - Base = new UniversalDamageHandler(damage, DeathTranslations.Hypothermia, cassieAnnouncement); - break; - case DamageType.Asphyxiation: - Base = new UniversalDamageHandler(damage, DeathTranslations.Asphyxiated, cassieAnnouncement); - break; - case DamageType.Poison: - Base = new UniversalDamageHandler(damage, DeathTranslations.Poisoned, cassieAnnouncement); - break; - case DamageType.Bleeding: - Base = new UniversalDamageHandler(damage, DeathTranslations.Bleeding, cassieAnnouncement); - break; - case DamageType.Crushed: - Base = new UniversalDamageHandler(damage, DeathTranslations.Crushed, cassieAnnouncement); - break; - case DamageType.FemurBreaker: - Base = new UniversalDamageHandler(damage, DeathTranslations.UsedAs106Bait, cassieAnnouncement); - break; - case DamageType.PocketDimension: - Base = new UniversalDamageHandler(damage, DeathTranslations.PocketDecay, cassieAnnouncement); - break; - case DamageType.FriendlyFireDetector: - Base = new UniversalDamageHandler(damage, DeathTranslations.FriendlyFireDetector, cassieAnnouncement); - break; - case DamageType.SeveredHands: - Base = new UniversalDamageHandler(damage, DeathTranslations.SeveredHands, cassieAnnouncement); - break; case DamageType.Warhead: Base = new WarheadDamageHandler(); break; - case DamageType.Decontamination: - Base = new UniversalDamageHandler(damage, DeathTranslations.Decontamination, cassieAnnouncement); - break; - case DamageType.Tesla: - Base = new UniversalDamageHandler(damage, DeathTranslations.Tesla, cassieAnnouncement); + case DamageType.Scp018: + Base = new Scp018DamageHandler(Pickup.Create(ItemType.SCP018).Cast().Base, damage, IgnoreFriendlyFire); break; case DamageType.Recontainment: Base = new RecontainmentDamageHandler(Attacker); @@ -108,92 +129,30 @@ public GenericDamageHandler(Player player, Player attacker, float damage, Damage Base = new JailbirdDamageHandler(Attacker.Hub, damage, Vector3.zero); break; case DamageType.MicroHid: - InventorySystem.Items.MicroHID.MicroHIDItem microHidOwner = new(); + MicroHIDItem microHidOwner = new GameObject().AddComponent(); microHidOwner.Owner = attacker.ReferenceHub; Base = new MicroHidDamageHandler(microHidOwner, damage); break; case DamageType.Explosion: Base = new ExplosionDamageHandler(attacker.Footprint, UnityEngine.Vector3.zero, damage, 0); break; - case DamageType.Firearm: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunAK); - break; - case DamageType.Crossvec: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunCrossvec); - break; - case DamageType.Logicer: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunLogicer); - break; - case DamageType.Revolver: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunRevolver); - break; - case DamageType.Shotgun: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunShotgun); - break; - case DamageType.AK: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunAK); - break; - case DamageType.Com15: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunCOM15); - break; - case DamageType.Com18: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunCOM18); - break; - case DamageType.Fsp9: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunFSP9); - break; - case DamageType.E11Sr: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunE11SR); - break; - case DamageType.Com45: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunCom45); - break; - case DamageType.Frmg0: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunFRMG0); - break; - case DamageType.A7: - GenericFirearm(player, attacker, damage, damageType, ItemType.GunA7); - break; case DamageType.ParticleDisruptor: Base = new DisruptorDamageHandler(Attacker, damage); break; case DamageType.Scp096: - Scp096Role curr096 = attacker.ReferenceHub.roleManager.CurrentRole as Scp096Role ?? new Scp096Role(); - - if (curr096 != null) - curr096._lastOwner = attacker.ReferenceHub; - - Base = new Scp096DamageHandler(curr096, damage, Scp096DamageHandler.AttackType.SlapRight); + Scp096Role curr096 = attacker.Role.Is(out Roles.Scp096Role scp096) ? scp096.Base : new GameObject().AddComponent(); + curr096._lastOwner = attacker.ReferenceHub; + Base = new Scp096DamageHandler(scp096.Base, damage, Scp096DamageHandler.AttackType.GateKill); break; case DamageType.Scp939: - Scp939Role curr939 = attacker.ReferenceHub.roleManager.CurrentRole as Scp939Role ?? new Scp939Role(); - - if (curr939 != null) - curr939._lastOwner = attacker.ReferenceHub; - + Scp939Role curr939 = attacker.Role.Is(out Roles.Scp939Role scp939) ? scp939.Base : new GameObject().AddComponent(); + curr939._lastOwner = attacker.ReferenceHub; Base = new Scp939DamageHandler(curr939, damage, Scp939DamageType.LungeTarget); break; - case DamageType.Scp: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.Unknown); - break; - case DamageType.Scp018: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.Unknown); - break; - case DamageType.Scp207: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.Scp207); - break; - case DamageType.Scp049: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.Scp049); - break; - case DamageType.Scp173: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.Scp173); - break; - case DamageType.Scp0492: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.Zombie); - break; - case DamageType.Scp106: - Base = new PlayerStatsSystem.ScpDamageHandler(attacker.ReferenceHub, damage, DeathTranslations.PocketDecay); + case DamageType.Strangled: + new Scp3114DamageHandler(attacker.ReferenceHub, damage, Scp3114DamageHandler.HandlerType.Strangulation); break; + case DamageType.Marshmallow: case DamageType.Custom: case DamageType.Unknown: default: @@ -217,6 +176,11 @@ public GenericDamageHandler(Player player, Player attacker, float damage, Damage /// public bool AllowSelfDamage { get; } + /// + /// Gets or sets a value indicating whether the friendly fire rules should be ignored. + /// + public bool IgnoreFriendlyFire { get; set; } + /// public override float Damage { get; set; } @@ -245,11 +209,11 @@ public override HandlerOutput ApplyDamage(ReferenceHub ply) /// /// Generic firearm path for handle type. /// - /// Current player. - /// Current attacker. - /// Damage amount. - /// Damage type. - /// ItemType. + /// Current player. + /// Current attacker. + /// Damage amount. + /// Damage type. + /// ItemType. private void GenericFirearm(Player player, Player attacker, float amount, DamageType damageType, ItemType itemType) { Firearm firearm = new(itemType) diff --git a/Exiled.API/Features/DamageHandlers/ScpDamageHandler.cs b/Exiled.API/Features/DamageHandlers/ScpDamageHandler.cs index ebeeedf616..688e1c81f4 100644 --- a/Exiled.API/Features/DamageHandlers/ScpDamageHandler.cs +++ b/Exiled.API/Features/DamageHandlers/ScpDamageHandler.cs @@ -8,9 +8,8 @@ namespace Exiled.API.Features.DamageHandlers { using Enums; - using Extensions; - + using PlayerRoles.PlayableScps.Scp939; using PlayerStatsSystem; using BaseHandler = PlayerStatsSystem.DamageHandlerBase; @@ -46,13 +45,15 @@ public override DamageType Type Scp049DamageHandler.AttackType.Scp0492 => DamageType.Scp0492, _ => DamageType.Scp049, }; + case Scp939DamageHandler: + return DamageType.Scp939; case BaseScpHandler scp: - { - DeathTranslation translation = DeathTranslations.TranslationsById[scp._translationId]; - if (translation.Id == DeathTranslations.PocketDecay.Id) - return DamageType.Scp106; - return DamageTypeExtensions.TranslationIdConversion.ContainsKey(translation.Id) ? DamageTypeExtensions.TranslationIdConversion[translation.Id] : DamageType.Scp; - } + { + DeathTranslation translation = DeathTranslations.TranslationsById[scp._translationId]; + return translation.Id == DeathTranslations.PocketDecay.Id ? DamageType.Scp106 : + DamageTypeExtensions.TranslationIdConversion.ContainsKey(translation.Id) ? + DamageTypeExtensions.TranslationIdConversion[translation.Id] : DamageType.Scp; + } default: return base.Type; diff --git a/Exiled.API/Features/Doors/Door.cs b/Exiled.API/Features/Doors/Door.cs index e4a2a6f487..a3172e2900 100644 --- a/Exiled.API/Features/Doors/Door.cs +++ b/Exiled.API/Features/Doors/Door.cs @@ -46,6 +46,7 @@ public class Door : GameEntity, IWrapper, IWorldSpace /// The base for this door. /// The of 's for this door. internal Door(DoorVariant door, List rooms) + : base() { Base = door; @@ -66,7 +67,7 @@ internal Door(DoorVariant door, List rooms) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => DoorVariantToDoor.Values; + public static new IReadOnlyCollection List => DoorVariantToDoor.Values; /// /// Gets the base-game corresponding with this door. @@ -78,11 +79,6 @@ internal Door(DoorVariant door, List rooms) /// public override GameObject GameObject => Base.gameObject; - /// - /// Gets the door's . - /// - public Transform Transform => Base.transform; - /// /// Gets the door's . /// @@ -185,13 +181,13 @@ public KeycardPermissions KeycardPermissions /// /// Gets or sets the door's position. /// - public Vector3 Position + public override Vector3 Position { - get => GameObject.transform.position; + get => Transform.position; set { NetworkServer.UnSpawn(GameObject); - GameObject.transform.position = value; + Transform.position = value; NetworkServer.Spawn(GameObject); } } @@ -220,7 +216,11 @@ public bool AllowsScp106 public DoorLockType DoorLockType { get => (DoorLockType)Base.NetworkActiveLocks; - set => ChangeLock(value); + set + { + Base.NetworkActiveLocks = (ushort)value; + DoorEvents.TriggerAction(Base, IsLocked ? DoorAction.Locked : DoorAction.Unlocked, null); + } } /// @@ -250,13 +250,13 @@ public DoorPermissions RequiredPermissions /// /// Gets or sets the door's rotation. /// - public Quaternion Rotation + public override Quaternion Rotation { - get => GameObject.transform.rotation; + get => Transform.rotation; set { NetworkServer.UnSpawn(GameObject); - GameObject.transform.rotation = value; + Transform.rotation = value; NetworkServer.Spawn(GameObject); } } @@ -270,7 +270,7 @@ public Vector3 Scale set { NetworkServer.UnSpawn(GameObject); - GameObject.transform.localScale = value; + Transform.localScale = value; NetworkServer.Spawn(GameObject); } } @@ -278,7 +278,7 @@ public Vector3 Scale /// /// Gets the door's . /// - public ZoneType Zone => Room?.Zone ?? ZoneType.Unspecified; + public ZoneType Zone => Room ? Room.Zone : ZoneType.Unspecified; /// /// Gets a containing all 's that are connected with . @@ -292,13 +292,11 @@ public Vector3 Scale /// A wrapper object. public static Door Get(DoorVariant doorVariant) { - if (doorVariant == null) + if (!doorVariant) return null; - if (doorVariant.Rooms == null) - { + if (doorVariant.Rooms is null) doorVariant.RegisterRooms(); - } // Exiled door must be created after the `RegisterRooms` call return DoorVariantToDoor[doorVariant]; @@ -323,10 +321,68 @@ public static Door Get(string name) public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInChildren()); /// - /// Gets a of filtered based on a predicate. + /// Gets a of containing all doors present in the specified . + /// + /// The zone from which looking for doors. + /// A of containing all doors present in the specified . + public static IEnumerable Get(ZoneType zoneType) => Get(door => door.Zone == zoneType); + + /// + /// Gets a of containing all doors present in the specified zones. + /// + /// The zones to retrieve the doors from. + /// A of containing all doors present in the specified zones. + public static IEnumerable Get(params ZoneType[] zoneTypes) => Get(door => zoneTypes.Contains(door.Zone)); + + /// + /// Gets a of containing all doors present in the specified zones. + /// + /// The zones to retrieve the doors from. + /// A of containing all doors present in the specified zones. + public static IEnumerable Get(IEnumerable zoneTypes) => Get(door => zoneTypes.Contains(door.Zone)); + + /// + /// Gets a of containing all doors present in the specified . + /// + /// The room to retrieve the doors from. + /// A of containing all doors present in the specified . + public static IEnumerable Get(Room room) => room.Doors; + + /// + /// Gets a of containing all doors present in the specified rooms. + /// + /// The rooms to retrieve the doors from. + /// A of containing all doors present in the specified rooms. + public static IEnumerable Get(params Room[] rooms) + { + HashSet result = new(); + + foreach (Room room in rooms) + result.AddRange(room.Doors); + + return result; + } + + /// + /// Gets a of containing all doors present in the specified rooms. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The rooms to retrieve the doors from. + /// A of containing all doors present in the specified rooms. + public static IEnumerable Get(IEnumerable rooms) + { + HashSet result = new(); + + foreach (Room room in rooms) + result.AddRange(room.Doors); + + return result; + } + + /// + /// Gets a of filtered given a predicate. + /// + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// @@ -334,7 +390,21 @@ public static Door Get(string name) /// /// The to search for. /// The with the given or if not found. - public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType); + public static Door Get(DoorType doorType) => List.FirstOrDefault(door => door.Type == doorType); + + /// + /// Gets a of containing all corresponding doors. + /// + /// The types to retrieve the doors from. + /// A of containing all corresponding doors. + public static IEnumerable Get(params DoorType[] types) => Get(door => types.Contains(door.Type)); + + /// + /// Gets a of containing all corresponding doors. + /// + /// The types to retrieve the doors from. + /// A of containing all corresponding doors. + public static IEnumerable Get(IEnumerable types) => Get(door => types.Contains(door.Type)); /// /// Returns the closest to the given . @@ -345,7 +415,6 @@ public static Door Get(string name) public static Door GetClosest(Vector3 position, out float distance) { Door doorToReturn = List.OrderBy(door => Vector3.Distance(position, door.Position)).FirstOrDefault(); - distance = Vector3.Distance(position, doorToReturn.Position); return doorToReturn; } @@ -358,78 +427,128 @@ public static Door GetClosest(Vector3 position, out float distance) /// object. public static Door Random(ZoneType type = ZoneType.Unspecified, bool onlyUnbroken = false) { - List doors = onlyUnbroken || type is not ZoneType.Unspecified ? Get(x => (x.Room is null || x.Room.Zone.HasFlag(type) || type == ZoneType.Unspecified) && (x is Breakable { IsDestroyed: true } || !onlyUnbroken)).ToList() : DoorVariantToDoor.Values.ToList(); + List doors = onlyUnbroken || type is not ZoneType.Unspecified ? Get(x => + (x.Room is null || x.Room.Zone.HasFlag(type) || type == ZoneType.Unspecified) && (x is Breakable { IsDestroyed: true } || !onlyUnbroken)). + ToList() : + DoorVariantToDoor.Values.ToList(); + return doors[UnityEngine.Random.Range(0, doors.Count)]; } /// - /// Locks all doors given the specified . + /// Permanently locks a corresponding to the given type. /// + /// The door to affect. + /// The specified . + public static void Lock(DoorType type, DoorLockType lockType = DoorLockType.Regular079) => Get(type)?.Lock(lockType); + + /// + /// Temporary locks a corresponding to the given type. + /// + /// The door to affect. /// The duration of the lockdown. - /// The to affect. /// The specified . - public static void LockAll(float duration, ZoneType zoneType = ZoneType.Unspecified, DoorLockType lockType = DoorLockType.Regular079) - { - foreach (Door door in Get(door => zoneType is not ZoneType.Unspecified && door.Zone.HasFlag(zoneType))) - { - door.IsOpen = false; - door.ChangeLock(lockType); - Timing.CallDelayed(duration, () => door.Unlock()); - } - } + public static void Lock(DoorType type, float duration, DoorLockType lockType = DoorLockType.Regular079) => Get(type)?.Lock(duration, lockType); + + /// + /// Unlocks a corresponding to the specified type. + /// + /// The . + public static void Unlock(DoorType type) => Get(type)?.Unlock(); /// - /// Locks all doors given the specified . + /// Locks a doors corresponding to the given type. /// + /// The door to affect. /// The duration of the lockdown. - /// The s to affect. /// The specified . - public static void LockAll(float duration, IEnumerable zoneTypes, DoorLockType lockType = DoorLockType.Regular079) - { - foreach (ZoneType zone in zoneTypes) - LockAll(duration, zone, lockType); - } + public static void LockAll(DoorType type, float duration, DoorLockType lockType = DoorLockType.Regular079) => Get(type)?.Lock(duration, lockType); + + /// + /// Permanently locks all doors in the facility. + /// + /// The specified . + public static void LockAll(DoorLockType lockType = DoorLockType.Regular079) => List.ForEach(door => door.Lock(lockType)); /// /// Locks all doors in the facility. /// /// The duration of the lockdown. /// The specified . - public static void LockAll(float duration, DoorLockType lockType = DoorLockType.Regular079) - { - foreach (Door door in List) - { - door.IsOpen = false; - door.ChangeLock(lockType); - Timing.CallDelayed(duration, () => door.Unlock()); - } - } + public static void LockAll(float duration, DoorLockType lockType = DoorLockType.Regular079) => List.ForEach(door => door.Lock(duration, lockType, true)); + + /// + /// Permanently locks all doors given the specified . + /// + /// The to affect. + /// The specified . + public static void LockAll(ZoneType type, DoorLockType lockType = DoorLockType.Regular079) => Get(type).ForEach(door => door.Lock(lockType, true)); + + /// + /// Permanently locks all doors given the specified zones. + /// + /// The zones to affect. + /// The specified . + public static void LockAll(IEnumerable types, DoorLockType lockType = DoorLockType.Regular079) => Get(types).ForEach(door => door.Lock(lockType, true)); + + /// + /// Temporary locks all doors given the specified . + /// + /// The to affect. + /// The duration of the lockdown. + /// The specified . + public static void LockAll(ZoneType type, float duration, DoorLockType lockType = DoorLockType.Regular079) => Get(type).ForEach(door => door.Lock(lockType, true)); + + /// + /// Temporary locks all doors given the specified zones. + /// + /// The zones to affect. + /// The duration of the lockdown. + /// The specified . + public static void LockAll(IEnumerable types, float duration, DoorLockType lockType = DoorLockType.Regular079) => types.ForEach(t => LockAll(t, duration, lockType)); + + /// + /// Permanently locks all doors corresponding to the given types. + /// + /// The doors to affect. + /// The specified . + public static void LockAll(IEnumerable types, DoorLockType lockType = DoorLockType.Regular079) => Get(types).ForEach(door => door.Lock(lockType, true)); + + /// + /// Temporary locks all doors corresponding to the given types. + /// + /// The doors to affect. + /// The duration of the lockdown. + /// The specified . + public static void LockAll(IEnumerable types, float duration, DoorLockType lockType = DoorLockType.Regular079) => types.ForEach(t => LockAll(t, duration, lockType)); /// /// Unlocks all doors in the facility. /// - public static void UnlockAll() - { - foreach (Door door in List) - door.Unlock(); - } + public static void UnlockAll() => List.ForEach(door => door.Unlock()); /// /// Unlocks all doors in the facility. /// - /// The to affect. - public static void UnlockAll(ZoneType zoneType) => UnlockAll(door => door.Zone.HasFlag(zoneType)); + /// The zones to affect. + public static void UnlockAll(ZoneType type) => UnlockAll(door => door.Zone.HasFlag(type)); /// /// Unlocks all doors in the facility. /// - /// The s to affect. - public static void UnlockAll(IEnumerable zoneTypes) => UnlockAll(door => zoneTypes.Contains(door.Zone)); + /// The zones to affect. + public static void UnlockAll(params ZoneType[] types) => UnlockAll(door => types.Contains(door.Zone)); /// /// Unlocks all doors in the facility. /// - /// The condition to satify. + /// The zones to affect. + public static void UnlockAll(IEnumerable types) => UnlockAll(door => types.Contains(door.Zone)); + + /// + /// Unlocks all doors in the facility. + /// + /// The condition to satisfy. public static void UnlockAll(Func predicate) { foreach (Door door in Get(predicate)) @@ -478,13 +597,27 @@ public void ChangeLock(DoorLockType lockType) } /// - /// Locks all active locks on the door, and then reverts back any changes after a specified length of time. + /// Permanently locks all active locks on the door, and then reverts back any changes after a specified length of time. /// - /// The amount of time that must pass before unlocking the door. /// The of the lockdown. - public void Lock(float time, DoorLockType lockType) + /// A value indicating whether the door should be closed. + public void Lock(DoorLockType lockType = DoorLockType.Regular079, bool shouldBeClosed = false) { + if (shouldBeClosed) + IsOpen = false; + ChangeLock(lockType); + } + + /// + /// Temporary locks all active locks on the door, and then reverts back any changes after a specified length of time. + /// + /// The amount of time that must pass before unlocking the door. + /// The of the lockdown. + /// A value indicating whether the door should be closed. + public void Lock(float time, DoorLockType lockType = DoorLockType.Regular079, bool shouldBeClosed = false) + { + Lock(lockType, shouldBeClosed); Unlock(time, lockType); } @@ -522,9 +655,7 @@ public void Lock(float time, DoorLockType lockType) internal static Door Create(DoorVariant doorVariant, List rooms) { if (doorVariant == null) - { return null; - } return doorVariant switch { @@ -545,35 +676,35 @@ private DoorType GetDoorType() string doorName = GameObject.name.GetBefore(' '); return doorName switch { - "LCZ" => Room?.Type switch + "LCZ" => Room switch { - RoomType.LczAirlock => (Base.GetComponentInParent() != null) ? DoorType.Airlock : DoorType.LightContainmentDoor, + { Type: RoomType.LczAirlock } => (Base.GetComponentInParent() != null) ? DoorType.Airlock : DoorType.LightContainmentDoor, _ => DoorType.LightContainmentDoor, }, "HCZ" => DoorType.HeavyContainmentDoor, "EZ" => DoorType.EntranceDoor, "Prison" => DoorType.PrisonDoor, "914" => DoorType.Scp914Door, - "Intercom" => Room?.Type switch + "Intercom" => Room switch { - RoomType.HczEzCheckpointA => DoorType.CheckpointArmoryA, - RoomType.HczEzCheckpointB => DoorType.CheckpointArmoryB, + { Type: RoomType.HczEzCheckpointA } => DoorType.CheckpointArmoryA, + { Type: RoomType.HczEzCheckpointB } => DoorType.CheckpointArmoryB, _ => DoorType.UnknownDoor, }, - "Unsecured" => Room?.Type switch + "Unsecured" => Room switch { - RoomType.EzCheckpointHallway => DoorType.CheckpointGate, - RoomType.Hcz049 => Position.y < -805 ? DoorType.Scp049Gate : DoorType.Scp173NewGate, + { Type: RoomType.EzCheckpointHallway } => DoorType.CheckpointGate, + { Type: RoomType.Hcz049 } => Position.y < -805 ? DoorType.Scp049Gate : DoorType.Scp173NewGate, _ => DoorType.UnknownGate, }, - "Elevator" => (Base as Interactables.Interobjects.ElevatorDoor)?.Group switch + "Elevator" => (Base as Interactables.Interobjects.ElevatorDoor) switch { - ElevatorGroup.Nuke => DoorType.ElevatorNuke, - ElevatorGroup.Scp049 => DoorType.ElevatorScp049, - ElevatorGroup.GateB => DoorType.ElevatorGateB, - ElevatorGroup.GateA => DoorType.ElevatorGateA, - ElevatorGroup.LczA01 or ElevatorGroup.LczA02 => DoorType.ElevatorLczA, - ElevatorGroup.LczB01 or ElevatorGroup.LczB02 => DoorType.ElevatorLczB, + { Group: ElevatorGroup.Nuke } => DoorType.ElevatorNuke, + { Group: ElevatorGroup.Scp049 } => DoorType.ElevatorScp049, + { Group: ElevatorGroup.GateB } => DoorType.ElevatorGateB, + { Group: ElevatorGroup.GateA } => DoorType.ElevatorGateA, + { Group: ElevatorGroup.LczA01 or ElevatorGroup.LczA02 } => DoorType.ElevatorLczA, + { Group: ElevatorGroup.LczB01 or ElevatorGroup.LczB02 } => DoorType.ElevatorLczB, _ => DoorType.UnknownElevator, }, _ => DoorType.UnknownDoor, diff --git a/Exiled.API/Features/DynamicEvents/DynamicEventDispatcher.cs b/Exiled.API/Features/DynamicEvents/DynamicEventDispatcher.cs index 11ca0d27ed..39d2e6df34 100644 --- a/Exiled.API/Features/DynamicEvents/DynamicEventDispatcher.cs +++ b/Exiled.API/Features/DynamicEvents/DynamicEventDispatcher.cs @@ -42,7 +42,7 @@ public DynamicEventDispatcher() /// This indexer allows access to bound listeners using an reference. /// /// The listener to look for. - /// The obund listener corresponding to the specified reference. + /// The bound listener corresponding to the specified reference. public KeyValuePair> this[object @object] => boundDelegates.FirstOrDefault(kvp => kvp.Key == @object); /// @@ -142,6 +142,19 @@ public virtual void Bind(object obj, Action del) /// The listener instance. public virtual void Unbind(object obj) => boundDelegates.Remove(obj); + /// + /// Unbinds a listener from the event dispatcher. + /// + /// The listener instance. + /// The delegate to be unbound. + public virtual void Unbind(object obj, Action del) + { + if (!boundDelegates.ContainsKey(obj)) + return; + + boundDelegates[obj].Remove(del); + } + /// /// Invokes the delegates from the specified listener. /// diff --git a/Exiled.API/Features/DynamicEvents/TDynamicEventDispatcher.cs b/Exiled.API/Features/DynamicEvents/TDynamicEventDispatcher.cs index f0c971d1ba..70ec8b0fa6 100644 --- a/Exiled.API/Features/DynamicEvents/TDynamicEventDispatcher.cs +++ b/Exiled.API/Features/DynamicEvents/TDynamicEventDispatcher.cs @@ -43,7 +43,7 @@ public TDynamicEventDispatcher() /// This indexer allows access to bound listeners using an reference. /// /// The listener to look for. - /// The obund listener corresponding to the specified reference. + /// The bound listener corresponding to the specified reference. public KeyValuePair>> this[object @object] => boundDelegates.FirstOrDefault(kvp => kvp.Key == @object); /// @@ -143,6 +143,19 @@ public virtual void Bind(object obj, Action del) /// The listener instance. public virtual void Unbind(object obj) => boundDelegates.Remove(obj); + /// + /// Unbinds a listener from the event dispatcher. + /// + /// The listener instance. + /// The delegate to be unbound. + public virtual void Unbind(object obj, Action del) + { + if (!boundDelegates.ContainsKey(obj)) + return; + + boundDelegates[obj].Remove(del); + } + /// /// Invokes the delegates from the specified listener. /// diff --git a/Exiled.API/Features/EConfig.cs b/Exiled.API/Features/EConfig.cs index 2f51f87221..f77b2f4487 100644 --- a/Exiled.API/Features/EConfig.cs +++ b/Exiled.API/Features/EConfig.cs @@ -70,6 +70,7 @@ public sealed class EConfig : TypeCastObject .WithTypeConverter(new AttachmentIdentifiersConverter()) .WithNamingConvention(UnderscoredNamingConvention.Instance) .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), deserializer => deserializer.InsteadOf()) + .WithDuplicateKeyChecking() .IgnoreFields() .IgnoreUnmatchedProperties() .Build(); @@ -102,7 +103,7 @@ public sealed class EConfig : TypeCastObject /// /// Gets the absolute path. /// - public string? AbsolutePath => Path.Combine(Paths.Configs, Path.Combine(Folder, Name)); + public string? AbsolutePath => Folder is not null && Name is not null ? Path.Combine(Paths.Configs, Path.Combine(Folder, Name)) : null; /// /// Gets a instance given the specified type . @@ -126,7 +127,7 @@ public sealed class EConfig : TypeCastObject /// /// The folder of the config to look for. /// The corresponding instance or if not found. - public static EConfig Get(string folder) => List.FirstOrDefault(cfg => cfg.Folder == folder); + public static EConfig Get(string folder) => List.FirstOrDefault(cfg => cfg.Folder == folder) ?? throw new InvalidOperationException(); /// /// Generates a new config of type . @@ -175,7 +176,7 @@ public static void LoadAll() if (attribute is null) return null; - ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); + ConstructorInfo? constructor = type.GetConstructor(Type.EmptyTypes); if (constructor is not null) { config = constructor.Invoke(null)!; @@ -227,26 +228,29 @@ public static void LoadAll() if (!wrapper!.Name!.Contains(".yml")) wrapper.Name += ".yml"; - string path = Path.Combine(Paths.Configs, wrapper.Folder); - if (attribute.IsParent) + if (wrapper.Folder is not null) { - if (!Directory.Exists(Path.Combine(path))) - Directory.CreateDirectory(path); + string path = Path.Combine(Paths.Configs, wrapper.Folder); + if (attribute.IsParent) + { + if (!Directory.Exists(Path.Combine(path))) + Directory.CreateDirectory(path); - Load(wrapper, wrapper.AbsolutePath!); - wrapper!.data!.Add(wrapper); - MainConfigsValue.Add(wrapper); + Load(wrapper, wrapper.AbsolutePath!); + wrapper!.data!.Add(wrapper); + MainConfigsValue.Add(wrapper); - Dictionary localCache = new(Cache); - foreach (KeyValuePair elem in localCache) - LoadFromCache(elem.Key); + Dictionary localCache = new(Cache); + foreach (KeyValuePair elem in localCache) + LoadFromCache(elem.Key); - return wrapper; - } + return wrapper; + } - Cache.Add(wrapper, wrapper.AbsolutePath!); - if (!Directory.Exists(path) || !MainConfigsValue.Any(cfg => cfg.Folder == wrapper.Folder)) - return wrapper; + Cache.Add(wrapper, wrapper.AbsolutePath!); + if (!Directory.Exists(path) || !MainConfigsValue.Any(cfg => cfg.Folder == wrapper.Folder)) + return wrapper; + } LoadFromCache(wrapper); @@ -303,12 +307,12 @@ public static void Load(EConfig config, string? path = null) path ??= config.AbsolutePath; if (File.Exists(path)) { - config.Base = Deserializer.Deserialize(File.ReadAllText(path), config.Base!.GetType())!; + config.Base = Deserializer.Deserialize(File.ReadAllText(path ?? throw new ArgumentNullException(nameof(path))), config.Base!.GetType())!; File.WriteAllText(path, Serializer.Serialize(config.Base!)); return; } - File.WriteAllText(path, Serializer.Serialize(config.Base!)); + File.WriteAllText(path ?? throw new ArgumentNullException(nameof(path)), Serializer.Serialize(config.Base!)); } /// @@ -345,12 +349,14 @@ public void Write(string name, object value) return; string? path = GetPath(); - PropertyInfo propertyInfo = param.Base!.GetType().GetProperty(name); + PropertyInfo? propertyInfo = param.Base!.GetType().GetProperty(name); if (propertyInfo is not null) { propertyInfo.SetValue(param, value); - File.WriteAllText(path, Serializer.Serialize(param)); - this.CopyProperties(Deserializer.Deserialize(File.ReadAllText(path), GetType())); + File.WriteAllText(path ?? throw new InvalidOperationException(), Serializer.Serialize(param)); + + if (path is not null) + this.CopyProperties(Deserializer.Deserialize(File.ReadAllText(path), GetType())); } } } diff --git a/Exiled.API/Features/Effect.cs b/Exiled.API/Features/Effect.cs index 84d8b4bf04..b0401d740a 100644 --- a/Exiled.API/Features/Effect.cs +++ b/Exiled.API/Features/Effect.cs @@ -31,8 +31,9 @@ public Effect() /// /// Get all the information of the effect>. public Effect(StatusEffectBase statusEffectBase) + : base() { - if (statusEffectBase.TryGetEffectType(out EffectType effect)) + if (!statusEffectBase.TryGetEffectType(out EffectType effect)) Log.Error($"EffectType not found please report to Exiled BugReport : {statusEffectBase}"); Type = effect; diff --git a/Exiled.API/Features/Generator.cs b/Exiled.API/Features/Generator.cs index 60581d1740..beb9b1b3ad 100644 --- a/Exiled.API/Features/Generator.cs +++ b/Exiled.API/Features/Generator.cs @@ -12,11 +12,10 @@ namespace Exiled.API.Features using System.Linq; using Enums; + using Exiled.API.Extensions; using Exiled.API.Features.Core; using Exiled.API.Interfaces; - using MapGeneration.Distributors; - using UnityEngine; /// @@ -35,6 +34,7 @@ public class Generator : GameEntity, IWrapper, IWorldSpace /// /// The . internal Generator(Scp079Generator scp079Generator) + : base() { Base = scp079Generator; Scp079GeneratorToGenerator.Add(scp079Generator, this); @@ -43,7 +43,13 @@ internal Generator(Scp079Generator scp079Generator) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => Scp079GeneratorToGenerator.Values; + public static new IReadOnlyCollection List => Scp079GeneratorToGenerator.Values; + + /// + /// Gets a randomly selected . + /// + /// A randomly selected object. + public static Generator Random => List.Random(); /// /// Gets the base . @@ -55,11 +61,6 @@ internal Generator(Scp079Generator scp079Generator) /// public override GameObject GameObject => Base.gameObject; - /// - /// Gets the of the generator. - /// - public Transform Transform => Base.transform; - /// /// Gets the generator's . /// @@ -204,12 +205,12 @@ public Player LastActivator /// /// Gets the generator position. /// - public Vector3 Position => Base.transform.position; + public override Vector3 Position => Transform.position; /// /// Gets the generator rotation. /// - public Quaternion Rotation => Base.transform.rotation; + public override Quaternion Rotation => Transform.rotation; /// /// Gets or sets the required permissions to interact with the generator. @@ -239,8 +240,8 @@ public static IEnumerable Get(GeneratorState state) /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// @@ -270,8 +271,8 @@ public static bool TryGet(GeneratorState state, out IEnumerable gener /// /// Try-get a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. /// Whether or not at least one generator was found. public static bool TryGet(Func predicate, out IEnumerable generators) { diff --git a/Exiled.API/Features/Hazards/Hazard.cs b/Exiled.API/Features/Hazards/Hazard.cs index c3b1a9f979..82d49810fe 100644 --- a/Exiled.API/Features/Hazards/Hazard.cs +++ b/Exiled.API/Features/Hazards/Hazard.cs @@ -32,6 +32,7 @@ public class Hazard : GameEntity, IWrapper /// /// The instance. public Hazard(EnvironmentalHazard hazard) + : base() { Base = hazard; @@ -41,7 +42,7 @@ public Hazard(EnvironmentalHazard hazard) /// /// Gets the list of all hazards. /// - public static IReadOnlyCollection List => EnvironmentalHazardToHazard.Values; + public static new IReadOnlyCollection List => EnvironmentalHazardToHazard.Values; /// /// Gets the . @@ -104,7 +105,7 @@ public Vector3 PositionOffset /// /// Gets or sets the position. /// - public Vector3 Position + public override Vector3 Position { get => Base.SourcePosition; set => Base.SourcePosition = value; diff --git a/Exiled.API/Features/Input/EventArgs/ProcessingActionEventArgs.cs b/Exiled.API/Features/Input/EventArgs/ProcessingActionEventArgs.cs index 5cb82de53b..b8fcd33614 100644 --- a/Exiled.API/Features/Input/EventArgs/ProcessingActionEventArgs.cs +++ b/Exiled.API/Features/Input/EventArgs/ProcessingActionEventArgs.cs @@ -13,7 +13,7 @@ namespace Exiled.API.Features.Input.EventArgs using Exiled.API.Features.Input; /// - /// Contains all informations before processing an action. + /// Contains all information before processing an action. /// public class ProcessingActionEventArgs : EventArgs { diff --git a/Exiled.API/Features/Input/InputActionComponent.cs b/Exiled.API/Features/Input/InputActionComponent.cs index 787ad72996..60e8f2f980 100644 --- a/Exiled.API/Features/Input/InputActionComponent.cs +++ b/Exiled.API/Features/Input/InputActionComponent.cs @@ -14,6 +14,7 @@ namespace Exiled.API.Features.Input using Exiled.API.Enums; using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; using Exiled.API.Features.DynamicEvents; using Exiled.API.Features.Input.EventArgs; diff --git a/Exiled.API/Features/Input/KeypressInputComponent.cs b/Exiled.API/Features/Input/KeypressInputComponent.cs index ffabacffa5..3d48d2ddb4 100644 --- a/Exiled.API/Features/Input/KeypressInputComponent.cs +++ b/Exiled.API/Features/Input/KeypressInputComponent.cs @@ -56,25 +56,25 @@ protected virtual void BindInputActions() /// /// The input condition, paired to , to be evaluated. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected virtual bool InputCondition_KT0() => false; /// /// The input condition, paired to , to be evaluated. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected virtual bool InputCondition_KT1() => false; /// /// The input condition, paired to , to be evaluated. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected virtual bool InputCondition_KT2() => false; /// /// The input condition, paired to , to be evaluated. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected virtual bool InputCondition_KT3() => false; /// diff --git a/Exiled.API/Features/Items/Item.cs b/Exiled.API/Features/Items/Item.cs index db7e112cfd..eb0a4c542d 100644 --- a/Exiled.API/Features/Items/Item.cs +++ b/Exiled.API/Features/Items/Item.cs @@ -48,6 +48,7 @@ public class Item : GameEntity, IWrapper /// /// The to encapsulate. public Item(ItemBase itemBase) + : base() { Base = itemBase; BaseToItem.Add(itemBase, this); @@ -78,7 +79,7 @@ internal Item(ItemType type) /// /// Gets a list of all 's on the server. /// - public static IEnumerable List => BaseToItem.Values; + public static new IEnumerable List => BaseToItem.Values; /// /// Gets or sets the unique serial number for the item. diff --git a/Exiled.API/Features/Items/Radio.cs b/Exiled.API/Features/Items/Radio.cs index 3207e085ff..ba1994120b 100644 --- a/Exiled.API/Features/Items/Radio.cs +++ b/Exiled.API/Features/Items/Radio.cs @@ -14,6 +14,8 @@ namespace Exiled.API.Features.Items using Structs; + using VoiceChat.Playbacks; + /// /// A wrapper class for . /// @@ -90,6 +92,11 @@ public bool IsEnabled set => Base._enabled = value; } + /// + /// Gets a value indicating whether or not the radio is transmitting. + /// + public bool IsTransmitting => PersonalRadioPlayback.IsTransmitting(Owner.ReferenceHub); + /// /// Sets the of the given . /// diff --git a/Exiled.API/Features/Items/Scp330.cs b/Exiled.API/Features/Items/Scp330.cs index d0a7e3b9c8..4dbd946058 100644 --- a/Exiled.API/Features/Items/Scp330.cs +++ b/Exiled.API/Features/Items/Scp330.cs @@ -87,6 +87,24 @@ internal Scp330() /// public CandyKindID ExposedType { get; set; } = CandyKindID.None; + /// + /// Gets or sets a index in of current selected candy. + /// + public int SelectedCandyId + { + get => Base.SelectedCandyId; + set + { + Base.SelectedCandyId = value; + Base.Owner.connectionToClient.Send(new SelectScp330Message + { + CandyID = value, + Drop = false, + Serial = Serial, + }); + } + } + /// /// Adds a specific candy to the bag. /// diff --git a/Exiled.API/Features/Lift.cs b/Exiled.API/Features/Lift.cs index a456882ee6..8ecfa0805a 100644 --- a/Exiled.API/Features/Lift.cs +++ b/Exiled.API/Features/Lift.cs @@ -14,11 +14,12 @@ namespace Exiled.API.Features using Exiled.API.Enums; using Exiled.API.Extensions; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Doors; - using Exiled.API.Features.Pools; using Exiled.API.Interfaces; using Interactables.Interobjects; using Interactables.Interobjects.DoorUtils; + using MEC; using UnityEngine; using static Interactables.Interobjects.ElevatorChamber; @@ -46,6 +47,7 @@ public class Lift : GameEntity, IWrapper, IWorldSpace /// /// The to wrap. internal Lift(ElevatorChamber elevator) + : base() { Base = elevator; ElevatorChamberToLift.Add(elevator, this); @@ -61,7 +63,7 @@ internal Lift(ElevatorChamber elevator) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => ElevatorChamberToLift.Values; + public static new IReadOnlyCollection List => ElevatorChamberToLift.Values; /// /// Gets a random . @@ -69,11 +71,6 @@ internal Lift(ElevatorChamber elevator) /// object. public static Lift Random => List.Random(); - /// - /// Gets the base . - /// - public ElevatorChamber Base { get; } - /// /// Gets a value of the internal doors list. /// @@ -94,29 +91,6 @@ internal Lift(ElevatorChamber elevator) /// public override GameObject GameObject => Base.gameObject; - /// - /// Gets the lift's . - /// - public Transform Transform => Base.transform; - - /// - /// Gets or sets the lift's position. - /// - public Vector3 Position - { - get => Base.transform.position; - set => Base.transform.position = value; - } - - /// - /// Gets or sets the lift's rotation. - /// - public Quaternion Rotation - { - get => Base.transform.rotation; - set => Base.transform.rotation = value; - } - /// /// Gets or sets the lift's status. /// @@ -204,6 +178,29 @@ public float AnimationTime /// public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination).As(); + /// + /// Gets or sets the lift's position. + /// + public override Vector3 Position + { + get => Transform.position; + set => Transform.position = value; + } + + /// + /// Gets or sets the lift's rotation. + /// + public override Quaternion Rotation + { + get => Transform.rotation; + set => Transform.rotation = value; + } + + /// + /// Gets the base . + /// + public ElevatorChamber Base { get; } + /// /// Gets a of which contains all the instances from the specified . /// @@ -223,7 +220,21 @@ public float AnimationTime /// /// The . /// A or if not found. - public static Lift Get(ElevatorType type) => Get(lift => lift.Type == type).FirstOrDefault(); + public static Lift Get(ElevatorType type) => List.FirstOrDefault(lift => lift.Type == type); + + /// + /// Gets all lifts corresponding to the specified types, if any. + /// + /// The types. + /// All corresponding lifts. + public static IEnumerable Get(params ElevatorType[] types) => Get(lift => types.Contains(lift.Type)); + + /// + /// Gets all lifts corresponding to the specified types, if any. + /// + /// The types. + /// All corresponding lifts. + public static IEnumerable Get(IEnumerable types) => Get(lift => types.Contains(lift.Type)); /// /// Gets the corresponding to the specified name, if any. @@ -249,10 +260,94 @@ public float AnimationTime /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); + /// + /// Permanently locks an elevator corresponding to the given type. + /// + /// The elevator to affect. + /// The specified . + public static void Lock(ElevatorType type, DoorLockReason lockReason = DoorLockReason.Isolation) => Get(type)?.Lock(lockReason); + + /// + /// Temporary locks an elevator corresponding to the given type. + /// + /// The elevator to affect. + /// The duration of the lockdown. + /// The specified . + public static void Lock(ElevatorType type, float duration, DoorLockReason lockReason = DoorLockReason.Isolation) => Get(type)?.Lock(duration, lockReason); + + /// + /// Unlocks a lift corresponding to the specified type. + /// + /// The . + public static void Unlock(ElevatorType type) => Get(type)?.Unlock(); + + /// + /// Permanently locks all elevators in the facility. + /// + /// The specified . + public static void LockAll(DoorLockReason lockReason = DoorLockReason.Isolation) => List.ForEach(lift => lift.Lock(lockReason)); + + /// + /// Temporary locks all elevators in the facility. + /// + /// The duration of the lockdown. + /// The specified . + public static void LockAll(float duration, DoorLockReason lockReason = DoorLockReason.Isolation) => List.ForEach(lift => lift.Lock(duration, lockReason)); + + /// + /// Permanently locks all elevators corresponding to the given types. + /// + /// The doors to affect. + /// The specified . + public static void LockAll(IEnumerable types, DoorLockReason lockReason = DoorLockReason.Isolation) => types.ForEach(t => Lock(t, lockReason)); + + /// + /// Temporary locks all elevators corresponding to the given types. + /// + /// The doors to affect. + /// The duration of the lockdown. + /// The specified . + public static void LockAll(IEnumerable types, float duration, DoorLockReason lockReason = DoorLockReason.Isolation) => types.ForEach(t => Lock(t, lockReason)); + + /// + /// Unlocks all lifts in the facility. + /// + public static void UnlockAll() => List.ForEach(lift => lift.Unlock()); + + /// + /// Unlocks all lifts in the facility. + /// + /// The zones to affect. + public static void UnlockAll(ZoneType type) => List.ForEach(lift => lift.Doors.Where(door => door.Zone == type).ForEach(door => door.Unlock())); + + /// + /// Unlocks all lifts in the facility. + /// + /// The zones to affect. + public static void UnlockAll(params ZoneType[] types) => List.ForEach(lift => lift.Doors.Where(door => types.Contains(door.Zone)).ForEach(door => door.Unlock())); + + /// + /// Unlocks all lifts in the facility. + /// + /// The zones to affect. + public static void UnlockAll(IEnumerable types) => List.ForEach(lift => lift.Doors.Where(door => types.Contains(door.Zone)).ForEach(door => door.Unlock())); + + /// + /// Unlocks all lifts in the facility. + /// + /// The types to affect. + public static void UnlockAll(params ElevatorType[] types) => Get(types).ForEach(lift => lift.Unlock()); + + /// + /// Unlocks all lifts in the facility. + /// + /// The types to affect. + public static void UnlockAll(IEnumerable types) => Get(types).ForEach(lift => lift.Unlock()); + /// /// Tries to melt a . /// @@ -277,10 +372,42 @@ public static bool TryMeltPlayer(Player player) /// if the lift was started successfully; otherwise, . public bool TryStart(int level, bool isForced = false) => TrySetDestination(Group, level, isForced); + /// + /// Locks the lift. + /// + /// The . + public void Lock(DoorLockReason lockReason = DoorLockReason.Isolation) + { + Status = ElevatorSequence.DoorClosing; + ChangeLock(lockReason); + } + + /// + /// Locks the lift. + /// + /// The duration of the lockdown. + /// The . + public void Lock(float duration, DoorLockReason lockReason = DoorLockReason.Isolation) + { + Status = ElevatorSequence.DoorClosing; + ChangeLock(lockReason); + } + + /// + /// Unlocks the lift. + /// + public void Unlock() => ChangeLock(DoorLockReason.None); + + /// + /// Unlocks the lift. + /// + /// The delay after which the lift should be unlocked. + public void Unlock(float delay) => Timing.CallDelayed(delay, () => ChangeLock(DoorLockReason.None)); + /// /// Changes lock of the lift. /// - /// Type of lift lockdown. + /// The . public void ChangeLock(DoorLockReason lockReason) { bool forceLock = lockReason != DoorLockReason.None; @@ -318,4 +445,4 @@ public void ChangeLock(DoorLockReason lockReason) /// A string containing Lift-related data. public override string ToString() => $"{Type} {Status} [{CurrentLevel}] *{IsLocked}*"; } -} +} \ No newline at end of file diff --git a/Exiled.API/Features/Npc.cs b/Exiled.API/Features/Npc.cs index a272fd3445..66e43baf78 100644 --- a/Exiled.API/Features/Npc.cs +++ b/Exiled.API/Features/Npc.cs @@ -13,21 +13,24 @@ namespace Exiled.API.Features using System.Linq; using System.Reflection; + using CentralAuth; using CommandSystem; using Exiled.API.Enums; using Exiled.API.Features.Components; - + using Exiled.API.Features.Roles; using Footprinting; - + using InventorySystem.Items.Firearms.BasicMessages; + using InventorySystem.Items.Firearms.Modules; using MEC; - using Mirror; using PlayerRoles; - + using PlayerRoles.FirstPersonControl; + using RelativePositioning; using UnityEngine; + using Firearm = Items.Firearm; using Object = UnityEngine.Object; /// @@ -137,7 +140,7 @@ public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId Npc npc = new(newObject) { - IsVerified = true, + IsVerified = userId != PlayerAuthenticationManager.DedicatedId && userId != null, IsNPC = true, }; @@ -161,7 +164,22 @@ public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId try { - npc.ReferenceHub.authManager.UserId = string.IsNullOrEmpty(userId) ? $"Dummy@localhost" : userId; + if (userId == PlayerAuthenticationManager.DedicatedId) + { + npc.ReferenceHub.authManager.SyncedUserId = userId; + try + { + npc.ReferenceHub.authManager.InstanceMode = ClientInstanceMode.DedicatedServer; + } + catch (Exception e) + { + Log.Debug($"Ignore: {e}"); + } + } + else + { + npc.ReferenceHub.authManager.UserId = userId == string.Empty ? $"Dummy@localhost" : userId; + } } catch (Exception e) { @@ -193,5 +211,95 @@ public void Destroy() Dictionary.Remove(GameObject); Object.Destroy(GameObject); } + + /// + /// Forces the NPC to look in the specified rotation. + /// + /// The position to look at. + public void LookAt(Vector3 position) + { + if (Role is FpcRole fpc) + { + Vector3 direction = position - Position; + Quaternion quat = Quaternion.LookRotation(direction, Vector3.up); + LookAt(quat); + } + } + + /// + /// Forces the NPC to look in the specified rotation. + /// + /// The rotation to look towards. + public void LookAt(Quaternion rotation) + { + if (Role is not FpcRole fpc) + return; + + if (rotation.eulerAngles.z != 0f) + rotation = Quaternion.LookRotation(rotation * Vector3.forward, Vector3.up); + + Vector2 angles = new Vector2(-rotation.eulerAngles.x, rotation.eulerAngles.y); + + ushort hor = (ushort)Mathf.RoundToInt(Mathf.Repeat(angles.y, 360f) * (ushort.MaxValue / 360f)); + ushort vert = (ushort)Mathf.RoundToInt(Mathf.Clamp(Mathf.Repeat(angles.x + 90f, 360f) - 2f, 0f, 176f) * (ushort.MaxValue / 176f)); + + fpc.FirstPersonController.FpcModule.MouseLook.ApplySyncValues(hor, vert); + } + + /// + /// Forces the NPC to shoot their current . + /// + /// if the weapon shot request is received. Returns otherwise, or if the player is not an or is not holding a . + public bool ShootWeapon() + { + if (CurrentItem is not Firearm firearm) + return false; + + if (!firearm.Base.ActionModule.ServerAuthorizeShot()) + return false; + + ShotMessage message = new ShotMessage() + { + ShooterCameraRotation = CameraTransform.rotation, + ShooterPosition = new RelativePosition(Transform.position), + ShooterWeaponSerial = CurrentItem.Serial, + TargetNetId = 0, + TargetPosition = default, + TargetRotation = Quaternion.identity, + }; + + Physics.Raycast(CameraTransform.position, CameraTransform.forward, out RaycastHit hit, firearm.Base.BaseStats.MaxDistance(), StandardHitregBase.HitregMask); + + if (hit.transform && hit.collider.TryGetComponent(out IDestructible destructible) && destructible != null) + { + message.TargetNetId = destructible.NetworkId; + message.TargetPosition = new RelativePosition(hit.transform.position); + message.TargetRotation = hit.transform.rotation; + } + else if (hit.transform) + { + message.TargetPosition = new RelativePosition(hit.transform.position); + message.TargetRotation = hit.transform.rotation; + } + + FirearmBasicMessagesHandler.ServerShotReceived(ReferenceHub.connectionToClient, message); + return true; + } + + /// + /// Sets the NPC's current status for Aiming Down Sights. + /// + /// Should the player be aiming down sights. + /// if the weapon Aim Down Sights request is received. Returns otherwise, or if the player is not an or is not holding a . + public bool SetAimDownSight(bool shouldADS) + { + if (CurrentItem is Firearm firearm) + { + FirearmBasicMessagesHandler.ServerRequestReceived(ReferenceHub.connectionToClient, new RequestMessage(firearm.Serial, shouldADS ? RequestType.AdsIn : RequestType.AdsOut)); + return true; + } + + return false; + } } } diff --git a/Exiled.API/Features/Pickups/Pickup.cs b/Exiled.API/Features/Pickups/Pickup.cs index ef703986a4..7d35f9ec56 100644 --- a/Exiled.API/Features/Pickups/Pickup.cs +++ b/Exiled.API/Features/Pickups/Pickup.cs @@ -11,16 +11,15 @@ 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; - using InventorySystem; using InventorySystem.Items; using InventorySystem.Items.Pickups; using InventorySystem.Items.ThrowableProjectiles; using InventorySystem.Items.Usables.Scp244; - using Mirror; using RelativePositioning; using UnityEngine; @@ -36,7 +35,6 @@ namespace Exiled.API.Features.Pickups using BaseScp1576Pickup = InventorySystem.Items.Usables.Scp1576.Scp1576Pickup; using BaseScp2176Projectile = InventorySystem.Items.ThrowableProjectiles.Scp2176Projectile; using BaseScp330Pickup = InventorySystem.Items.Usables.Scp330.Scp330Pickup; - using Object = UnityEngine.Object; /// @@ -56,6 +54,7 @@ public class Pickup : GameEntity, IWrapper, IWorldSpace /// Created only for properly work. /// internal Pickup() + : base() { } @@ -64,6 +63,7 @@ internal Pickup() /// /// The base class. internal Pickup(ItemPickupBase pickupBase) + : base() { Base = pickupBase; @@ -104,17 +104,18 @@ internal Pickup(ItemType type) /// /// Gets a of which contains all the instances. /// - public static IEnumerable List => BaseToPickup.Values; + public static new IEnumerable List => BaseToPickup.Values; /// - /// Gets the of the Pickup. + /// Gets a randomly selected . /// - public override GameObject GameObject => GameObject; + /// A randomly selected object. + public static Pickup Random => BaseToPickup.Random().Value; /// - /// Gets the of the Pickup. + /// Gets the of the Pickup. /// - public Transform Transform => Base.transform; + public override GameObject GameObject => GameObject; /// /// Gets the of the Pickup. @@ -263,7 +264,7 @@ public bool InUse /// Gets or sets the pickup position. /// /// - public Vector3 Position + public override Vector3 Position { get => Base.Position; set => Base.Position = value; @@ -274,7 +275,7 @@ public Vector3 Position /// public RelativePosition RelativePosition { - get => new(Room.transform.TransformPoint(Position)); + get => new(Room.Transform.TransformPoint(Position)); set => Position = value.Position; } @@ -282,7 +283,7 @@ public RelativePosition RelativePosition /// Gets or sets the pickup rotation. /// /// - public Quaternion Rotation + public override Quaternion Rotation { get => Base.Rotation; set => Base.Rotation = value; diff --git a/Exiled.API/Features/Pickups/Scp244Pickup.cs b/Exiled.API/Features/Pickups/Scp244Pickup.cs index f852fcc3ae..082d63f248 100644 --- a/Exiled.API/Features/Pickups/Scp244Pickup.cs +++ b/Exiled.API/Features/Pickups/Scp244Pickup.cs @@ -124,10 +124,10 @@ public float ActivationDot } /// - /// Damages the Scp244Pickup. + /// Damages the . /// /// The used to deal damage. - /// if the the damage has been deal; otherwise, . + /// if the the damage has been dealt; otherwise, . public bool Damage(DamageHandler handler) => Base.Damage(handler.Damage, handler, Vector3.zero); /// diff --git a/Exiled.API/Features/Player.cs b/Exiled.API/Features/Player.cs index c3b6009e84..3c180239c5 100644 --- a/Exiled.API/Features/Player.cs +++ b/Exiled.API/Features/Player.cs @@ -17,11 +17,12 @@ namespace Exiled.API.Features using CustomPlayerEffects; using DamageHandlers; using Enums; + using Exiled.API.Features.Attributes; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Doors; using Exiled.API.Features.Hazards; using Exiled.API.Features.Items; using Exiled.API.Features.Pickups; - using Exiled.API.Features.Pools; using Exiled.API.Features.Roles; using Exiled.API.Interfaces; using Exiled.API.Structs; @@ -73,9 +74,17 @@ namespace Exiled.API.Features /// /// Represents the in-game player, by encapsulating a . /// - public class Player : GameEntity, IWorldSpace + [DefaultPlayerClass] + public class Player : GameEntity { #pragma warning disable SA1401 +#pragma warning disable SA1310 + /// + /// The default player class. + /// + internal static Type DEFAULT_PLAYER_CLASS = typeof(Player); +#pragma warning restore SA1310 + /// /// A list of the player's items. /// @@ -91,6 +100,7 @@ public class Player : GameEntity, IWorldSpace /// /// The of the player to be encapsulated. public Player(ReferenceHub referenceHub) + : base() { ReferenceHub = referenceHub; Items = ItemsValue.AsReadOnly(); @@ -101,6 +111,7 @@ public Player(ReferenceHub referenceHub) /// /// The of the player. public Player(GameObject gameObject) + : base() { ReferenceHub = ReferenceHub.GetHub(gameObject); Items = ItemsValue.AsReadOnly(); @@ -124,13 +135,19 @@ public Player(GameObject gameObject) /// /// Gets a list of all 's on the server. /// - public static IReadOnlyCollection List => Dictionary.Values; + public static new IReadOnlyCollection List => Dictionary.Values; /// /// Gets a containing cached and their user ids. /// public static Dictionary UserIdsCache { get; } = new(20); + /// + /// Gets a randomly selected . + /// + /// A randomly selected object. + public static Player Random => List.Random(); + /// /// Gets or sets a containing cached and their FF multiplier. This is for non-unique roles. /// @@ -155,7 +172,7 @@ public ReferenceHub ReferenceHub get => referenceHub; private set { - referenceHub = value ?? throw new NullReferenceException("Player's ReferenceHub cannot be null!"); + referenceHub = value ? value : throw new NullReferenceException("Player's ReferenceHub cannot be null!"); GameObject = value.gameObject; HintDisplay = value.hints; Inventory = value.inventory; @@ -178,7 +195,7 @@ private set /// /// Gets the 's . /// - public Transform Transform => ReferenceHub.transform; + public override Transform Transform => ReferenceHub.transform; /// /// Gets the hint currently watched by the player. @@ -493,7 +510,7 @@ public Player Cuffer /// /// /// - public Vector3 Position + public override Vector3 Position { get => Transform.position; set => ReferenceHub.TryOverridePosition(value, Vector3.zero); @@ -513,7 +530,7 @@ public RelativePosition RelativePosition /// Gets or sets the player's rotation. /// /// Returns the direction the player is looking at. - public Quaternion Rotation + public override Quaternion Rotation { get => Transform.rotation; set => ReferenceHub.TryOverridePosition(Position, value.eulerAngles); @@ -773,6 +790,11 @@ public bool IsIntercomMuted /// public bool IsSpeaking => VoiceModule != null && VoiceModule.IsSpeaking; + /// + /// Gets the loudness of a player when speaking. + /// + public float Loudness => !IsSpeaking || VoiceModule is not StandardVoiceModule standardModule ? 0f : standardModule.GlobalPlayback.Loudness; + /// /// Gets the player's voice color. /// @@ -919,7 +941,12 @@ public Item CurrentItem } if (!Inventory.UserInventory.Items.TryGetValue(value.Serial, out _)) + { + if (IsInventoryFull) + return; + AddItem(value.Base); + } Inventory.ServerSelectItem(value.Serial); } @@ -967,7 +994,7 @@ public string GroupName /// /// Gets the current zone the player is in. /// - public ZoneType Zone => CurrentRoom?.Zone ?? ZoneType.Unspecified; + public ZoneType Zone => CurrentRoom ? CurrentRoom.Zone : ZoneType.Unspecified; /// /// Gets the current the player is in. Can be . @@ -1055,7 +1082,7 @@ public bool BadgeHidden /// /// Gets a value indicating whether or not the player is in the pocket dimension. /// - public bool IsInPocketDimension => CurrentRoom?.Type is RoomType.Pocket; + public bool IsInPocketDimension => CurrentRoom && CurrentRoom.Type is RoomType.Pocket; /// /// Gets or sets a value indicating whether or not the player should use stamina system. @@ -1157,6 +1184,38 @@ public bool IsSpawnProtected /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); + /// + /// Gets the nearest player to the specified within the given distance. + /// + /// The to compare. + /// The maximum distance the player can be from the to be included. + /// The nearest within the specified distance, or if no player is found. + public static Player GetNearestPlayer(Vector3 vector, float distance) => GetNearestPlayers(vector, distance).OrderBy(p => (vector - p.Position).sqrMagnitude).FirstOrDefault(); + + /// + /// Gets all players near the specified within the given distance. + /// + /// The to compare. + /// The maximum distance the player can be from the to be included. + /// The filtered collection of objects. + public static IEnumerable GetNearestPlayers(Vector3 vector, float distance) => List.Where(p => (vector - p.Position).sqrMagnitude <= distance * distance); + + /// + /// Gets the farthest player from the specified within the given distance. + /// + /// The to compare. + /// The minimum distance the player can be from the to be included. + /// The farthest from the specified within the given distance, or if no player is found. + public static Player GetFarthestPlayer(Vector3 vector, float distance) => GetFarthestPlayers(vector, distance).OrderByDescending(p => (vector - p.Position).sqrMagnitude).FirstOrDefault(); + + /// + /// Gets all players that have a distance greater than the specified distance from the given . + /// + /// The to compare. + /// The minimum distance the player can be from the to be included. + /// The filtered collection of objects. + public static IEnumerable GetFarthestPlayers(Vector3 vector, float distance) => List.Where(p => (vector - p.Position).sqrMagnitude >= distance * distance); + /// /// Gets the belonging to the , if any. /// @@ -1195,6 +1254,13 @@ public static Player Get(ReferenceHub referenceHub) } } + /// + /// Gets the all instances belonging to the given instances, if any. + /// + /// The reference hubs to retrieve the players from. + /// All instances belonging to the given instances. + public static IEnumerable Get(IEnumerable hubs) => hubs.Select(hub => Get(hub)); + /// /// Gets the belonging to the , if any. /// @@ -1415,6 +1481,24 @@ public static Player Get(string args) /// A boolean indicating whether or not a player was found. public static bool TryGet(Collider collider, out Player player) => (player = Get(collider)) is not null; + /// + /// Gets an containing all players processed based on the arguments specified. + /// + /// The array segment of strings representing the input arguments to be processed. + /// The starting index within the array segment. + /// Contains the updated arguments after processing. + /// Determines whether empty entries should be kept in the result. + /// An representing the processed players. + public static IEnumerable GetProcessedData(ArraySegment args, int startIndex, out string[] newargs, bool keepEmptyEntries = false) => RAUtils.ProcessPlayerIdOrNamesList(args, startIndex, out newargs, keepEmptyEntries).Select(hub => Get(hub)); + + /// + /// Gets an containing all players processed based on the arguments specified. + /// + /// The array segment of strings representing the input arguments to be processed. + /// The starting index within the array segment. + /// An representing the processed players. + public static IEnumerable GetProcessedData(ArraySegment args, int startIndex = 0) => GetProcessedData(args, startIndex, out string[] _); + /// /// Adds a player's UserId to the list of reserved slots. /// @@ -1723,7 +1807,7 @@ public void TrySetCustomRoleFriendlyFire(string roleTypeId, Dictionary CustomRoleFriendlyFireMultiplier.Remove(role); /// - /// Forces the player to reload their current weapon. + /// Forces the player to reload their current . /// /// if firearm was successfully reloaded. Otherwise, . public bool ReloadWeapon() @@ -1731,13 +1815,45 @@ public bool ReloadWeapon() if (CurrentItem is Firearm firearm) { bool result = firearm.Base.AmmoManagerModule.ServerTryReload(); - Connection.Send(new RequestMessage(firearm.Serial, RequestType.Reload)); + if (result) + Connection.Send(new RequestMessage(firearm.Serial, RequestType.Reload)); return result; } return false; } + /// + /// Forces the player to unload their current . + /// + /// if the weapon unload request is received. Returns otherwise, or if the player is not an or is not holding a . + public bool UnloadWeapon() + { + if (CurrentItem is Firearm firearm) + { + bool result = firearm.Base.AmmoManagerModule.ServerTryUnload(); + if (result) + Connection.Send(new RequestMessage(firearm.Serial, RequestType.Unload)); + return result; + } + + return true; + } + + /// + /// Forces the player to toggle the Flashlight Attachment on their current . + /// + /// if the weapon flashlight toggle request is received. Returns otherwise, or if the player is not an or is not holding a . + public bool ToggleWeaponFlashlight() + { + if (RoleManager.CurrentRole is not IFpcRole fpc || CurrentItem is not Firearm firearm) + return false; + + bool oldCheck = firearm.FlashlightEnabled; // Temporary Solution + FirearmBasicMessagesHandler.ServerRequestReceived(ReferenceHub.connectionToClient, new RequestMessage(firearm.Serial, RequestType.ToggleFlashlight)); + return oldCheck != firearm.FlashlightEnabled; + } + /// /// Tries to get an item from a player's inventory. /// @@ -1751,6 +1867,19 @@ public bool TryGetItem(ushort serial, out Item item) return item != null; } + /// + /// Tries to get an items from a player's inventory. + /// + /// The predicate to satisfy. + /// The found. + /// if the item is found, otherwise. + public bool TryGetItems(Func predicate, out IEnumerable items) + { + items = Items.Where(predicate); + + return items.Count() != 0; + } + /// /// Sets the player's rank. /// @@ -1939,12 +2068,7 @@ public bool RemoveItem(Item item, bool destroy = true) /// The serial to remove. /// Whether or not to destroy the item. /// A value indicating whether or not the was removed. - public bool RemoveItem(ushort serial, bool destroy = true) - { - if (Items.SingleOrDefault(item => item.Serial == serial) is not Item item) - return false; - return RemoveItem(item, destroy); - } + public bool RemoveItem(ushort serial, bool destroy = true) => Items.SingleOrDefault(item => item.Serial == serial) is Item item && RemoveItem(item, destroy); /// /// Removes an from the player's inventory. @@ -2037,6 +2161,12 @@ public int GetScpPreference(RoleTypeId roleType) /// The used to deal damage. public void Hurt(DamageHandlerBase damageHandlerBase) => ReferenceHub.playerStats.DealDamage(damageHandlerBase); + /// + /// Hurts the player. + /// + /// The used to deal damage. + public void Hurt(DamageHandlers.DamageHandlerBase damageHandlerBase) => ReferenceHub.playerStats.DealDamage(damageHandlerBase.Base); + /// /// Hurts the player. /// @@ -2207,7 +2337,7 @@ public void Ban(int duration, string reason, Player issuer = null) public void UnMute(bool isIntercom = false) => VoiceChatMutes.RevokeLocalMute(UserId, isIntercom); /// - /// Blink the player's tag. + /// Blinks the player's tag. /// /// Used to wait. public IEnumerator BlinkTag() @@ -3280,6 +3410,21 @@ public void Teleport(object obj, Vector3 offset) ? new Vector3(3, 0, 0) : new Vector3(0, 0, 3))); break; + case Role role: + if (role.Owner is not null) + Teleport(role.Owner.Position + offset); + else + Log.Warn($"{nameof(Teleport)}: {Assembly.GetCallingAssembly().GetName().Name}: Invalid role teleport (role is missing Owner)."); + break; + case Item item: + if (item.Owner is not null) + Teleport(item.Owner.Position + offset); + else + Log.Warn($"{nameof(Teleport)}: {Assembly.GetCallingAssembly().GetName().Name}: Invalid item teleport (item is missing Owner)."); + break; + case GameEntity entity: + Teleport(entity.Position + Vector3.up + offset); + break; case IPosition positionObject: Teleport(positionObject.Position + Vector3.up + offset); break; @@ -3301,12 +3446,6 @@ public void Teleport(object obj, Vector3 offset) case Scp914Controller scp914: Teleport(scp914._knobTransform.position + Vector3.up + offset); break; - case Role role: - if (role.Owner is not null) - Teleport(role.Owner.Position + offset); - else - Log.Warn($"{nameof(Teleport)}: {Assembly.GetCallingAssembly().GetName().Name}: Invalid role teleport (role is missing Owner)."); - break; case Locker locker: Teleport(locker.transform.position + Vector3.up + offset); break; @@ -3316,12 +3455,6 @@ public void Teleport(object obj, Vector3 offset) case ElevatorChamber elevator: Teleport(elevator.transform.position + Vector3.up + offset); break; - case Item item: - if (item.Owner is not null) - Teleport(item.Owner.Position + offset); - else - Log.Warn($"{nameof(Teleport)}: {Assembly.GetCallingAssembly().GetName().Name}: Invalid item teleport (item is missing Owner)."); - break; // Unity case Vector3 v3: // I wouldn't be surprised if someone calls this method with a Vector3. @@ -3348,16 +3481,16 @@ public void RandomTeleport(Type type) { object randomObject = type.Name switch { - nameof(Camera) => Camera.List.Random(), + nameof(Camera) => Camera.Random, nameof(Door) => Door.Random(), - nameof(Room) => Room.List.Random(), - nameof(TeslaGate) => TeslaGate.List.Random(), - nameof(Player) => Dictionary.Values.Random(), - nameof(Pickup) => Pickup.BaseToPickup.Random().Value, - nameof(Ragdoll) => Ragdoll.List.Random(), + nameof(Room) => Room.Random(), + nameof(TeslaGate) => TeslaGate.Random, + nameof(Player) => Random, + nameof(Pickup) => Pickup.Random, + nameof(Ragdoll) => Ragdoll.Random, nameof(Locker) => Map.GetRandomLocker(), - nameof(Generator) => Generator.List.Random(), - nameof(Window) => Window.List.Random(), + nameof(Generator) => Generator.Random, + nameof(Window) => Window.Random, nameof(Scp914) => Scp914.Scp914Controller, nameof(LockerChamber) => Map.GetRandomLocker().Chambers.Random(), _ => null, @@ -3387,37 +3520,37 @@ public void RandomTeleport(IEnumerable types) public void RandomTeleport() => RandomTeleport(typeof(T)); /// - /// Get the time cooldown on this ItemType. + /// Retrieves the cooldown time for the specified ItemType. /// - /// The itemtypes to choose for getting cooldown. - /// Return the time in seconds of the cooldowns. + /// The ItemType to retrieve the cooldown for. + /// The cooldown time in seconds, or -1 if not found. public float GetCooldownItem(ItemType itemType) => UsableItemsController.GetHandler(ReferenceHub).PersonalCooldowns.TryGetValue(itemType, out float value) ? value : -1; /// - /// Set the time cooldown on this ItemType. + /// Sets the cooldown time for the specified ItemType. /// - /// The times for the cooldown. - /// The itemtypes to choose for being cooldown. + /// The cooldown time in seconds. + /// The ItemType to set the cooldown for. public void SetCooldownItem(float time, ItemType itemType) => UsableItemsController.GetHandler(ReferenceHub).PersonalCooldowns[itemType] = Time.timeSinceLevelLoad + time; /// - /// Explode the player. + /// Triggers an explosion for the player. /// public void Explode() => ExplosionUtils.ServerExplode(ReferenceHub); /// - /// Explode the player. + /// Triggers an explosion for the player. /// - /// The projectile that will create the explosion. - /// The Player that will causing the explosion. + /// The type of projectile causing the explosion. + /// The player triggering the explosion. public void Explode(ProjectileType projectileType, Player attacker = null) => Map.Explode(Position, projectileType, attacker); /// - /// Spawn projectile effect on the player. + /// Spawns an explosion effect for the player. /// - /// The projectile that will create the effect. + /// The type of projectile creating the effect. public void ExplodeEffect(ProjectileType projectileType) => Map.ExplodeEffect(Position, projectileType); /// diff --git a/Exiled.API/Features/Ragdoll.cs b/Exiled.API/Features/Ragdoll.cs index 34669fd1d9..89c4ede2b7 100644 --- a/Exiled.API/Features/Ragdoll.cs +++ b/Exiled.API/Features/Ragdoll.cs @@ -49,6 +49,7 @@ public class Ragdoll : GameEntity, IWrapper, IWorldSpace /// /// The encapsulated . internal Ragdoll(BasicRagdoll ragdoll) + : base() { Base = ragdoll; BasicRagdollToRagdoll.Add(ragdoll, this); @@ -57,7 +58,7 @@ internal Ragdoll(BasicRagdoll ragdoll) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => BasicRagdollToRagdoll.Values; + public static new IReadOnlyCollection List => BasicRagdollToRagdoll.Values; /// /// Gets or sets the s clean up time. @@ -68,6 +69,12 @@ public static int FreezeTime set => RagdollManager.FreezeTime = value; } + /// + /// Gets a randomly selected . + /// + /// A randomly selected object. + public static Ragdoll Random => List.Random(); + /// /// Gets a value indicating whether or not the clean up event can be executed. /// @@ -83,11 +90,6 @@ public static int FreezeTime /// public override GameObject GameObject => Base.gameObject; - /// - /// Gets the of the ragdoll. - /// - public Transform Transform => Base.transform; - /// /// Gets or sets the ragdoll's . /// @@ -220,14 +222,14 @@ public bool IsConsumed /// /// Gets or sets the ragdoll's position. /// - public Vector3 Position + public override Vector3 Position { - get => Base.transform.position; + get => Transform.position; set { NetworkServer.UnSpawn(GameObject); - Base.transform.position = value; + Transform.position = value; NetworkServer.Spawn(GameObject); } @@ -236,14 +238,14 @@ public Vector3 Position /// /// Gets or sets the ragdoll's rotation. /// - public Quaternion Rotation + public override Quaternion Rotation { get => Base.transform.rotation; set { NetworkServer.UnSpawn(GameObject); - Base.transform.rotation = value; + Transform.rotation = value; NetworkServer.Spawn(GameObject); } @@ -254,12 +256,12 @@ public Quaternion Rotation /// public Vector3 Scale { - get => Base.transform.localScale; + get => Transform.localScale; set { NetworkServer.UnSpawn(GameObject); - Base.transform.localScale = value; + Transform.localScale = value; NetworkServer.Spawn(GameObject); } @@ -307,6 +309,7 @@ public static bool TryCreate(RagdollData networkInfo, out Ragdoll ragdoll) Position = networkInfo.StartPosition, Rotation = networkInfo.StartRotation, }; + return true; } @@ -320,7 +323,7 @@ public static bool TryCreate(RagdollData networkInfo, out Ragdoll ragdoll) /// The optional owner of the ragdoll. /// The ragdoll. public static bool TryCreate(RoleTypeId roleType, string name, DamageHandlerBase damageHandler, out Ragdoll ragdoll, Player owner = null) - => TryCreate(new(owner?.ReferenceHub ?? Server.Host.ReferenceHub, damageHandler, roleType, default, default, name, NetworkTime.time), out ragdoll); + => TryCreate(new(owner?.ReferenceHub ? owner?.ReferenceHub : Server.Host.ReferenceHub, damageHandler, roleType, default, default, name, NetworkTime.time), out ragdoll); /// /// Creates a new ragdoll. @@ -360,7 +363,7 @@ public static Ragdoll CreateAndSpawn(RagdollData networkInfo) /// The optional owner of the ragdoll. /// The ragdoll. public static Ragdoll CreateAndSpawn(RoleTypeId roleType, string name, DamageHandlerBase damageHandler, Vector3 position, Quaternion rotation, Player owner = null) - => CreateAndSpawn(new(owner?.ReferenceHub ?? Server.Host.ReferenceHub, damageHandler, roleType, position, rotation, name, NetworkTime.time)); + => CreateAndSpawn(new(owner?.ReferenceHub ? owner?.ReferenceHub : Server.Host.ReferenceHub, damageHandler, roleType, position, rotation, name, NetworkTime.time)); /// /// Creates and spawns a new ragdoll. diff --git a/Exiled.API/Features/Respawn.cs b/Exiled.API/Features/Respawn.cs index 3367de9c3b..674eb4f2f6 100644 --- a/Exiled.API/Features/Respawn.cs +++ b/Exiled.API/Features/Respawn.cs @@ -157,7 +157,12 @@ public static bool ProtectedCanShoot public static void PlayEffects(byte[] effects) { foreach (RespawnEffectsController controller in RespawnEffectsController.AllControllers) - controller?.RpcPlayEffects(effects); + { + if (!controller) + continue; + + controller.RpcPlayEffects(effects); + } } /// @@ -177,17 +182,15 @@ public static void PlayEffects(byte[] effects) /// Whether or not to play the Chaos Insurgency spawn music. public static void SummonChaosInsurgencyVan(bool playMusic = true) { - PlayEffects( - playMusic - ? new[] - { - RespawnEffectType.PlayChaosInsurgencyMusic, - RespawnEffectType.SummonChaosInsurgencyVan, - } - : new[] - { - RespawnEffectType.SummonChaosInsurgencyVan, - }); + PlayEffects(playMusic ? new[] + { + RespawnEffectType.PlayChaosInsurgencyMusic, + RespawnEffectType.SummonChaosInsurgencyVan, + } + : new[] + { + RespawnEffectType.SummonChaosInsurgencyVan, + }); } /// diff --git a/Exiled.API/Features/Roles/FpcRole.cs b/Exiled.API/Features/Roles/FpcRole.cs index 27abeded8a..443423776c 100644 --- a/Exiled.API/Features/Roles/FpcRole.cs +++ b/Exiled.API/Features/Roles/FpcRole.cs @@ -9,14 +9,12 @@ namespace Exiled.API.Features.Roles { using System.Collections.Generic; - using Exiled.API.Features.Pools; - + using Exiled.API.Extensions; + using Exiled.API.Features.Core.Generic.Pools; using PlayerRoles; using PlayerRoles.FirstPersonControl; - using PlayerStatsSystem; using RelativePositioning; - using UnityEngine; /// @@ -25,6 +23,7 @@ namespace Exiled.API.Features.Roles public abstract class FpcRole : Role { private bool isUsingStamina = true; + private RoleTypeId fakeAppearance; /// /// Initializes a new instance of the class. @@ -218,6 +217,23 @@ public bool IsNoclipEnabled set => Owner.ReferenceHub.playerStats.GetModule().SetFlag(AdminFlags.Noclip, value); } + /// + /// Gets or sets a value indicating the fake appearance of the player. + /// + public RoleTypeId? FakeAppearance + { + get => fakeAppearance; + set + { + fakeAppearance = value.Value; + + if (value.HasValue) + Owner.ChangeAppearance(value.Value); + else + Owner.ChangeAppearance(Owner.Role.Type, skipJump: true); + } + } + /// /// Resets the 's stamina. /// diff --git a/Exiled.API/Features/Roles/Role.cs b/Exiled.API/Features/Roles/Role.cs index 7787908f1a..a7d38bdabc 100644 --- a/Exiled.API/Features/Roles/Role.cs +++ b/Exiled.API/Features/Roles/Role.cs @@ -8,18 +8,18 @@ namespace Exiled.API.Features.Roles { using System; + using System.Collections.Generic; + using System.Linq; using Enums; - using Exiled.API.Features.Core; using Exiled.API.Features.Spawn; using Exiled.API.Interfaces; using Extensions; - using PlayerRoles; using PlayerRoles.PlayableScps.Scp049.Zombies; - using UnityEngine; + using YamlDotNet.Serialization.TypeInspectors; using FilmmakerGameRole = PlayerRoles.Filmmaker.FilmmakerRole; using HumanGameRole = PlayerRoles.HumanRole; @@ -43,6 +43,7 @@ public abstract class Role : GameEntity, IWrapper /// /// the base . protected Role(PlayerRoleBase baseRole) + : base() { if (baseRole.TryGetOwner(out ReferenceHub hub)) Owner = Player.Get(hub); @@ -50,6 +51,16 @@ protected Role(PlayerRoleBase baseRole) Base = baseRole; } + /// + /// Gets a random human . + /// + public static RoleTypeId RandomHuman => Enum.GetValues(typeof(RoleTypeId)).ToArray().Shuffle().FirstOrDefault(role => role.IsHuman()); + + /// + /// Gets a random human . + /// + public static RoleTypeId RandomScp => Enum.GetValues(typeof(RoleTypeId)).ToArray().Shuffle().FirstOrDefault(role => RoleExtensions.GetTeam(role) == Team.SCPs); + /// public override GameObject GameObject => Base.gameObject; @@ -178,6 +189,29 @@ protected Role(PlayerRoleBase baseRole) /// if the values are not equal. public static bool operator !=(RoleTypeId type, Role role) => role != type; + /// + /// Gets a random . + /// + /// Specifies whether non-playable roles should be included. + /// An optional collection of role types to exclude. + /// A random . + public static RoleTypeId Random(bool includeNonPlayableRoles = false, IEnumerable except = null) + { + RoleTypeId[] roles = Enum.GetValues(typeof(RoleTypeId)).ToArray(); + + if (!includeNonPlayableRoles) + { + IEnumerable exceptRoles = roles.Except(new RoleTypeId[] { RoleTypeId.Filmmaker, RoleTypeId.None, RoleTypeId.Overwatch, RoleTypeId.Spectator }); + + if (except is not null) + exceptRoles = exceptRoles.Except(except); + + return exceptRoles.Shuffle().First(); + } + + return includeNonPlayableRoles && except is not null ? roles.Except(except).Shuffle().First() : roles.Shuffle().First(); + } + /// public override bool Equals(object obj) => base.Equals(obj); diff --git a/Exiled.API/Features/Roles/Scp939Role.cs b/Exiled.API/Features/Roles/Scp939Role.cs index b6dca72b82..87135ccfd4 100644 --- a/Exiled.API/Features/Roles/Scp939Role.cs +++ b/Exiled.API/Features/Roles/Scp939Role.cs @@ -10,7 +10,7 @@ namespace Exiled.API.Features.Roles using System.Collections.Generic; using Exiled.API.Enums; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using PlayerRoles; using PlayerRoles.PlayableScps.HumeShield; diff --git a/Exiled.API/Features/Room.cs b/Exiled.API/Features/Room.cs index b3b112b642..da1885ad8c 100644 --- a/Exiled.API/Features/Room.cs +++ b/Exiled.API/Features/Room.cs @@ -13,6 +13,7 @@ namespace Exiled.API.Features using Enums; using Exiled.API.Extensions; + using Exiled.API.Features.Core; using Exiled.API.Features.Doors; using Exiled.API.Features.Pickups; using Exiled.API.Interfaces; @@ -27,42 +28,80 @@ namespace Exiled.API.Features /// /// The in-game room. /// - public class Room : MonoBehaviour, IWorldSpace + public class Room : GameEntity, IWorldSpace { /// /// A containing all known s and their corresponding . /// internal static readonly Dictionary RoomIdentifierToRoom = new(250); + private GameObject gameObject; + /// - /// Gets a of which contains all the instances. + /// Initializes a new instance of the class. /// - public static IReadOnlyCollection List => RoomIdentifierToRoom.Values; + /// The room's . + internal Room(GameObject go) + { + gameObject = go; + + Identifier = gameObject.GetComponent(); + RoomIdentifierToRoom.Add(Identifier, this); + + Zone = FindZone(gameObject); +#if Debug + if (Type is RoomType.Unknown) + Log.Error($"[ZONETYPE UNKNOWN] {this}"); +#endif + Type = FindType(gameObject); +#if Debug + if (Type is RoomType.Unknown) + Log.Error($"[ROOMTYPE UNKNOWN] {this}"); +#endif + + RoomLightControllersValue.AddRange(gameObject.GetComponentsInChildren()); + + RoomLightControllers = RoomLightControllersValue.AsReadOnly(); + + GameObject.GetComponentsInChildren().ForEach(component => + { + Window window = new(component, this); + window.Room.WindowsValue.Add(window); + }); + + if (GameObject.GetComponentInChildren() is global::TeslaGate tesla) + TeslaGate = new TeslaGate(tesla, this); + + Windows = WindowsValue.AsReadOnly(); + Doors = DoorsValue.AsReadOnly(); + Speakers = SpeakersValue.AsReadOnly(); + Cameras = CamerasValue.AsReadOnly(); + } /// - /// Gets the name. + /// Gets a of which contains all the instances. /// - public string Name => name; + public static new IReadOnlyCollection List => RoomIdentifierToRoom.Values; /// /// Gets the . /// - public GameObject GameObject => gameObject; + public override GameObject GameObject => gameObject; /// - /// Gets the . + /// Gets the name. /// - public Transform Transform => transform; + public string Name => GameObject.name; /// - /// Gets the position. + /// Gets the 's position. /// - public Vector3 Position => transform.position; + public override Vector3 Position => Transform.position; /// - /// Gets the rotation. + /// Gets the 's rotation. /// - public Quaternion Rotation => transform.rotation; + public override Quaternion Rotation => Transform.rotation; /// /// Gets the in which the room is located. @@ -168,7 +207,7 @@ public bool AreLightsOff /// /// Gets the FlickerableLightController's NetworkIdentity. /// - public NetworkIdentity RoomLightControllerNetIdentity => RoomLightController?.netIdentity; + public NetworkIdentity RoomLightControllerNetIdentity => RoomLightController ? RoomLightController.netIdentity : null; /// /// Gets the room's FlickerableLightController. @@ -246,8 +285,8 @@ public static Room Get(RoomIdentifier roomIdentifier) => roomIdentifier == null /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// @@ -283,7 +322,7 @@ public static Room FindParentRoom(GameObject objectInRoom) } // Finally, try for objects that aren't children, like players and pickups. - return room ?? Get(objectInRoom.transform.position) ?? default; + return room ? room : Get(objectInRoom.transform.position) ?? default; } /// @@ -334,8 +373,6 @@ public void TurnOffLights(float duration = -1) /// /// Duration in seconds, or -1 for permanent lockdown. /// DoorLockType of the lockdown. - /// - /// public void LockDown(float duration, DoorLockType lockType = DoorLockType.Regular079) { foreach (Door door in Doors) @@ -387,16 +424,6 @@ public void UnlockAll() /// A string containing Room-related data. public override string ToString() => $"{Type} ({Zone}) [{Doors.Count}] *{Cameras.Count}* |{TeslaGate != null}|"; - /// - /// Factory method to create and add a component to a Transform. - /// We can add parameters to be set privately here. - /// - /// The Game Object to attach the Room component to. - internal static void CreateComponent(GameObject roomGameObject) - { - roomGameObject.AddComponent().InternalCreate(); - } - private static RoomType FindType(GameObject gameObject) { // Try to remove brackets if they exist. @@ -467,51 +494,19 @@ private static ZoneType FindZone(GameObject gameObject) { Transform transform = gameObject.transform; - return transform.parent?.name.RemoveBracketsOnEndOfName() switch - { - "HeavyRooms" => ZoneType.HeavyContainment, - "LightRooms" => ZoneType.LightContainment, - "EntranceRooms" => ZoneType.Entrance, - "HCZ_EZ_Checkpoint" => ZoneType.HeavyContainment | ZoneType.Entrance, - _ => transform.position.y > 900 ? ZoneType.Surface : ZoneType.Unspecified, - }; - } - - private void InternalCreate() - { - Identifier = gameObject.GetComponent(); - RoomIdentifierToRoom.Add(Identifier, this); - - Zone = FindZone(gameObject); -#if Debug - if (Type is RoomType.Unknown) - Log.Error($"[ZONETYPE UNKNOWN] {this}"); -#endif - Type = FindType(gameObject); -#if Debug - if (Type is RoomType.Unknown) - Log.Error($"[ROOMTYPE UNKNOWN] {this}"); -#endif - - RoomLightControllersValue.AddRange(gameObject.GetComponentsInChildren()); - - RoomLightControllers = RoomLightControllersValue.AsReadOnly(); - - GetComponentsInChildren().ForEach(component => + if (transform && transform.parent) { - Window window = new(component, this); - window.Room.WindowsValue.Add(window); - }); - - if (GetComponentInChildren() is global::TeslaGate tesla) - { - TeslaGate = new TeslaGate(tesla, this); + return transform.parent.name.RemoveBracketsOnEndOfName() switch + { + "HeavyRooms" => ZoneType.HeavyContainment, + "LightRooms" => ZoneType.LightContainment, + "EntranceRooms" => ZoneType.Entrance, + "HCZ_EZ_Checkpoint" => ZoneType.HeavyContainment | ZoneType.Entrance, + _ => transform.position.y > 900 ? ZoneType.Surface : ZoneType.Unspecified, + }; } - Windows = WindowsValue.AsReadOnly(); - Doors = DoorsValue.AsReadOnly(); - Speakers = SpeakersValue.AsReadOnly(); - Cameras = CamerasValue.AsReadOnly(); + return ZoneType.Unspecified; } } } diff --git a/Exiled.API/Features/Round.cs b/Exiled.API/Features/Round.cs index 7f811a56be..c32b863e38 100644 --- a/Exiled.API/Features/Round.cs +++ b/Exiled.API/Features/Round.cs @@ -11,7 +11,7 @@ namespace Exiled.API.Features using System.Collections.Generic; using Enums; - + using Exiled.API.Extensions; using GameCore; using PlayerRoles; @@ -23,6 +23,8 @@ namespace Exiled.API.Features /// public static class Round { + private static int targetOffset; + /// /// Gets a list of players who will be ignored from determining round end. /// @@ -44,7 +46,7 @@ public static class Round /// /// Gets a value indicating whether the round is started or not. /// - public static bool IsStarted => ReferenceHub.LocalHub?.characterClassManager.RoundStarted ?? false; + public static bool IsStarted => ReferenceHub.LocalHub && ReferenceHub.LocalHub.characterClassManager.RoundStarted; /// /// Gets a value indicating whether the round in progress or not. @@ -183,6 +185,22 @@ public static IEnumerable AliveSides } } + /// + /// Gets or sets a visual offset applied to the target counter for SCPs. + /// + public static int TargetOffset + { + get => targetOffset; + set + { + targetOffset = value; + foreach (Player player in Player.List) + { + player.SendFakeSyncVar(RoundSummary.singleton.netIdentity, typeof(RoundSummary), nameof(RoundSummary.Network_chaosTargetCount), RoundSummary.singleton._chaosTargetCount + TargetOffset); + } + } + } + /// /// Restarts the round with custom settings. /// diff --git a/Exiled.API/Features/Scp3114Ragdoll.cs b/Exiled.API/Features/Scp3114Ragdoll.cs index 145e65a20f..494ec7cdc7 100644 --- a/Exiled.API/Features/Scp3114Ragdoll.cs +++ b/Exiled.API/Features/Scp3114Ragdoll.cs @@ -10,25 +10,25 @@ namespace Exiled.API.Features using Exiled.API.Interfaces; using PlayerRoles; - using BaseRagdoll = PlayerRoles.PlayableScps.Scp3114.Scp3114Ragdoll; + using BaseScp3114Ragdoll = PlayerRoles.PlayableScps.Scp3114.Scp3114Ragdoll; /// /// A wrapper for SCP-3114 ragdolls. /// - public class Scp3114Ragdoll : Ragdoll, IWrapper + public class Scp3114Ragdoll : Ragdoll, IWrapper { /// /// Initializes a new instance of the class. /// /// The base ragdoll to wrap. - internal Scp3114Ragdoll(BaseRagdoll ragdoll) + internal Scp3114Ragdoll(BaseScp3114Ragdoll ragdoll) : base(ragdoll) { Base = ragdoll; } /// - public new BaseRagdoll Base { get; } + public new BaseScp3114Ragdoll Base { get; } /// /// Gets or sets the role that the corpse is disguised as. diff --git a/Exiled.API/Features/Scp914.cs b/Exiled.API/Features/Scp914.cs index 93ef42e37f..c344f397e3 100644 --- a/Exiled.API/Features/Scp914.cs +++ b/Exiled.API/Features/Scp914.cs @@ -10,9 +10,9 @@ namespace Exiled.API.Features using System.Collections.Generic; using System.Linq; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Doors; using Exiled.API.Features.Pickups; - using Exiled.API.Features.Pools; using global::Scp914; using UnityEngine; diff --git a/Exiled.API/Features/Serialization/CustomConverters/ColorConverter.cs b/Exiled.API/Features/Serialization/CustomConverters/ColorConverter.cs index 8cd3669ab2..24e1b4e965 100644 --- a/Exiled.API/Features/Serialization/CustomConverters/ColorConverter.cs +++ b/Exiled.API/Features/Serialization/CustomConverters/ColorConverter.cs @@ -12,7 +12,7 @@ namespace Exiled.API.Features.Serialization.CustomConverters using System.Globalization; using System.IO; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using UnityEngine; diff --git a/Exiled.API/Features/Serialization/CustomConverters/VectorsConverter.cs b/Exiled.API/Features/Serialization/CustomConverters/VectorsConverter.cs index fa94628339..855f99ac87 100644 --- a/Exiled.API/Features/Serialization/CustomConverters/VectorsConverter.cs +++ b/Exiled.API/Features/Serialization/CustomConverters/VectorsConverter.cs @@ -12,7 +12,7 @@ namespace Exiled.API.Features.Serialization.CustomConverters using System.Globalization; using System.IO; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using UnityEngine; diff --git a/Exiled.API/Features/TeslaGate.cs b/Exiled.API/Features/TeslaGate.cs index 20d234568f..239369af3a 100644 --- a/Exiled.API/Features/TeslaGate.cs +++ b/Exiled.API/Features/TeslaGate.cs @@ -11,15 +11,13 @@ namespace Exiled.API.Features using System.Collections.Generic; using System.Linq; + using Exiled.API.Extensions; using Exiled.API.Features.Core; using Exiled.API.Interfaces; - using Hazards; - + using MapGeneration; using MEC; - using PlayerRoles; - using UnityEngine; using BaseTeslaGate = global::TeslaGate; @@ -40,6 +38,7 @@ public class TeslaGate : GameEntity, IWrapper, IWorldSpace /// The instance. /// The for this tesla. internal TeslaGate(BaseTeslaGate baseTeslaGate, Room room) + : base() { Base = baseTeslaGate; BaseTeslaGateToTeslaGate.Add(baseTeslaGate, this); @@ -49,7 +48,7 @@ internal TeslaGate(BaseTeslaGate baseTeslaGate, Room room) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => BaseTeslaGateToTeslaGate.Values; + public static new IReadOnlyCollection List => BaseTeslaGateToTeslaGate.Values; /// /// Gets or sets a of which contains all the players ignored by tesla gates. @@ -66,6 +65,12 @@ internal TeslaGate(BaseTeslaGate baseTeslaGate, Room room) /// public static List IgnoredTeams { get; set; } = new(); + /// + /// Gets a randomly selected . + /// + /// A randomly selected object. + public static TeslaGate Random => List.Random(); + /// /// Gets the base . /// @@ -76,20 +81,15 @@ internal TeslaGate(BaseTeslaGate baseTeslaGate, Room room) /// public override GameObject GameObject => Base.gameObject; - /// - /// Gets the tesla gate's . - /// - public Transform Transform => Base.transform; - /// /// Gets the tesla gate's position. /// - public Vector3 Position => Transform.position; + public override Vector3 Position => Transform.position; /// /// Gets the tesla gate's rotation. /// - public Quaternion Rotation => Quaternion.Euler(Base.localRotation); + public override Quaternion Rotation => Quaternion.Euler(Base.localRotation); /// /// Gets the tesla gate's which is located in. @@ -212,8 +212,8 @@ public static TeslaGate Get(BaseTeslaGate baseTeslaGate) => BaseTeslaGateToTesla /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// @@ -231,8 +231,8 @@ public static bool TryGet(BaseTeslaGate baseTeslaGate, out TeslaGate gate) /// /// Try-get a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. /// Whether or not at least one tesla gate was found. public static bool TryGet(Func predicate, out IEnumerable gates) { diff --git a/Exiled.API/Features/Toys/AdminToy.cs b/Exiled.API/Features/Toys/AdminToy.cs index c7dab3f599..67a2101efd 100644 --- a/Exiled.API/Features/Toys/AdminToy.cs +++ b/Exiled.API/Features/Toys/AdminToy.cs @@ -14,6 +14,7 @@ namespace Exiled.API.Features.Toys using Enums; using Exiled.API.Features.Core; using Exiled.API.Interfaces; + using Footprinting; using Mirror; using UnityEngine; @@ -29,6 +30,7 @@ public abstract class AdminToy : GameEntity, IWorldSpace /// The to be wrapped. /// The of the object. internal AdminToy(AdminToyBase toyAdminToyBase, AdminToyType type) + : base() { AdminToyBase = toyAdminToyBase; ToyType = type; @@ -50,21 +52,21 @@ internal AdminToy(AdminToyBase toyAdminToyBase, AdminToyType type) public AdminToyType ToyType { get; } /// - /// Gets or sets the position of the toy. + /// Gets or sets who spawn the Primitive AdminToy. /// - public Vector3 Position + public Player Player { - get => AdminToyBase.transform.position; - set => AdminToyBase.transform.position = value; + get => Player.Get(Footprint); + set => Footprint = value.Footprint; } /// - /// Gets or sets the rotation of the toy. + /// Gets or sets the Footprint of the player who spawned the AdminToy. /// - public Quaternion Rotation + public Footprint Footprint { - get => AdminToyBase.transform.rotation; - set => AdminToyBase.transform.rotation = value; + get => AdminToyBase.SpawnerFootprint; + set => AdminToyBase.SpawnerFootprint = value; } /// @@ -72,8 +74,8 @@ public Quaternion Rotation /// public Vector3 Scale { - get => AdminToyBase.transform.localScale; - set => AdminToyBase.transform.localScale = value; + get => Transform.localScale; + set => Transform.localScale = value; } /// @@ -89,6 +91,15 @@ public byte MovementSmoothing set => AdminToyBase.NetworkMovementSmoothing = value; } + /// + /// Gets or sets a value indicating whether IsStatic. + /// + public bool IsStatic + { + get => AdminToyBase.IsStatic; + set => AdminToyBase.IsStatic = value; + } + /// /// Gets the belonging to the . /// diff --git a/Exiled.API/Features/Toys/Light.cs b/Exiled.API/Features/Toys/Light.cs index b88f4eb428..903a5c5c1a 100644 --- a/Exiled.API/Features/Toys/Light.cs +++ b/Exiled.API/Features/Toys/Light.cs @@ -80,9 +80,9 @@ public bool ShadowEmission /// The scale of the . /// Whether the should be initially spawned. /// The new . - public static Light Create(Vector3? position = null, Vector3? rotation = null, Vector3? scale = null, bool spawn = true) - => Create(position, rotation, scale, spawn, null); - + public static Light Create(Vector3? position = null, Vector3? rotation = null, Vector3? scale = null, bool spawn = true) + => Create(position, rotation, scale, spawn, null); + /// /// Creates a new . /// @@ -96,9 +96,10 @@ public static Light Create(Vector3? position /*= null*/, Vector3? rotation /*= n { Light light = new(UnityEngine.Object.Instantiate(ToysHelper.LightBaseObject)); - light.Base.transform.position = position ?? Vector3.zero; - light.Base.transform.eulerAngles = rotation ?? Vector3.zero; - light.Base.transform.localScale = scale ?? Vector3.one; + Transform transform = light.Base.transform; + transform.position = position ?? Vector3.zero; + transform.eulerAngles = rotation ?? Vector3.zero; + transform.localScale = scale ?? Vector3.one; if (spawn) light.Spawn(); diff --git a/Exiled.API/Features/Toys/Primitive.cs b/Exiled.API/Features/Toys/Primitive.cs index ddb95da330..5a878dbdf2 100644 --- a/Exiled.API/Features/Toys/Primitive.cs +++ b/Exiled.API/Features/Toys/Primitive.cs @@ -111,9 +111,10 @@ public static Primitive Create(Vector3? position /*= null*/, Vector3? rotation / { Primitive primitive = new(Object.Instantiate(ToysHelper.PrimitiveBaseObject)); - primitive.Base.transform.position = position ?? Vector3.zero; - primitive.Base.transform.eulerAngles = rotation ?? Vector3.zero; - primitive.Base.transform.localScale = scale ?? Vector3.one; + Transform transform = primitive.Base.transform; + transform.position = position ?? Vector3.zero; + transform.eulerAngles = rotation ?? Vector3.zero; + transform.localScale = scale ?? Vector3.one; if (spawn) primitive.Spawn(); @@ -138,9 +139,10 @@ public static Primitive Create(PrimitiveType primitiveType /*= PrimitiveType.Sph { Primitive primitive = new(Object.Instantiate(ToysHelper.PrimitiveBaseObject)); - primitive.Base.transform.position = position ?? Vector3.zero; - primitive.Base.transform.eulerAngles = rotation ?? Vector3.zero; - primitive.Base.transform.localScale = scale ?? Vector3.one; + Transform transform = primitive.Base.transform; + transform.position = position ?? Vector3.zero; + transform.eulerAngles = rotation ?? Vector3.zero; + transform.localScale = scale ?? Vector3.one; if (spawn) primitive.Spawn(); @@ -161,9 +163,10 @@ public static Primitive Create(PrimitiveSettings primitiveSettings) { Primitive primitive = new(Object.Instantiate(ToysHelper.PrimitiveBaseObject)); - primitive.Base.transform.position = primitiveSettings.Position; - primitive.Base.transform.eulerAngles = primitiveSettings.Rotation; - primitive.Base.transform.localScale = primitiveSettings.Scale; + Transform transform = primitive.Base.transform; + transform.position = primitiveSettings.Position; + transform.eulerAngles = primitiveSettings.Rotation; + transform.localScale = primitiveSettings.Scale; if (primitiveSettings.Spawn) primitive.Spawn(); @@ -171,6 +174,7 @@ public static Primitive Create(PrimitiveSettings primitiveSettings) primitive.Base.NetworkScale = primitive.Base.transform.localScale; primitive.Base.NetworkPrimitiveType = primitiveSettings.PrimitiveType; primitive.Color = primitiveSettings.Color; + primitive.IsStatic = primitiveSettings.IsStatic; return primitive; } diff --git a/Exiled.API/Features/Toys/ShootingTargetToy.cs b/Exiled.API/Features/Toys/ShootingTargetToy.cs index 9f8408a18a..054b58bfae 100644 --- a/Exiled.API/Features/Toys/ShootingTargetToy.cs +++ b/Exiled.API/Features/Toys/ShootingTargetToy.cs @@ -177,9 +177,10 @@ public static ShootingTargetToy Create(ShootingTargetType type, Vector3? positio } } - shootingTargetToy.Base.transform.position = position ?? Vector3.zero; - shootingTargetToy.Base.transform.eulerAngles = rotation ?? Vector3.zero; - shootingTargetToy.Base.transform.localScale = scale ?? Vector3.one; + Transform transform = shootingTargetToy.Base.transform; + transform.position = position ?? Vector3.zero; + transform.eulerAngles = rotation ?? Vector3.zero; + transform.localScale = scale ?? Vector3.one; if (spawn) shootingTargetToy.Spawn(); diff --git a/Exiled.API/Features/VirtualAssemblies/Generics/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/Generics/VirtualPlugin.cs index 361048cc89..4a7161943f 100644 --- a/Exiled.API/Features/VirtualAssemblies/Generics/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/Generics/VirtualPlugin.cs @@ -5,11 +5,8 @@ // // ----------------------------------------------------------------------- -namespace Exiled.API.Features.VirtualAssemblies +namespace Exiled.API.Features.VirtualAssemblies.Generics { - using Exiled.API.Features; - using Exiled.API.Interfaces; - /// /// The type of the plugin's config. public abstract class VirtualPlugin : VirtualPlugin diff --git a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs index 5af54e7db6..686532ac33 100644 --- a/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs +++ b/Exiled.API/Features/VirtualAssemblies/VirtualPlugin.cs @@ -17,7 +17,7 @@ namespace Exiled.API.Features.VirtualAssemblies using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Core; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.DynamicEvents; using Exiled.API.Features.VirtualAssemblies.EventArgs; using Exiled.API.Interfaces; diff --git a/Exiled.API/Features/Warhead.cs b/Exiled.API/Features/Warhead.cs index 241d94c735..2438f223a7 100644 --- a/Exiled.API/Features/Warhead.cs +++ b/Exiled.API/Features/Warhead.cs @@ -51,6 +51,15 @@ public static bool AutoDetonate set => Controller._autoDetonate = value; } + /// + /// Gets or sets the auto detonation time. + /// + public static float AutoDetonateTime + { + get => Controller._autoDetonateTime; + set => Controller._autoDetonateTime = value; + } + /// /// Gets or sets a value indicating whether or not doors will be opened when the warhead activates. /// @@ -135,7 +144,7 @@ public static float DetonationTimer public static float RealDetonationTimer => Controller.CurScenario.TimeToDetonate; /// - /// Gets or sets a value indicating whether or not the warhead can be disabled. + /// Gets or sets a value indicating whether the warhead should be disabled. /// public static bool IsLocked { diff --git a/Exiled.API/Features/Window.cs b/Exiled.API/Features/Window.cs index 7ca533090e..a1819c9f57 100644 --- a/Exiled.API/Features/Window.cs +++ b/Exiled.API/Features/Window.cs @@ -13,6 +13,7 @@ namespace Exiled.API.Features using DamageHandlers; using Enums; + using Exiled.API.Extensions; using Exiled.API.Features.Core; using Exiled.API.Features.Doors; using Exiled.API.Interfaces; @@ -34,6 +35,7 @@ public class Window : GameEntity, IWrapper, IWorldSpace /// The base for this door. /// The for this window. internal Window(BreakableWindow window, Room room) + : base() { BreakableWindowToWindow.Add(window, this); Base = window; @@ -48,7 +50,13 @@ internal Window(BreakableWindow window, Room room) /// /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => BreakableWindowToWindow.Values; + public static new IReadOnlyCollection List => BreakableWindowToWindow.Values; + + /// + /// Gets a randomly selected . + /// + /// A randomly selected object. + public static Window Random => List.Random(); /// /// Gets the base-game for this window. @@ -63,7 +71,7 @@ internal Window(BreakableWindow window, Room room) /// /// Gets the window's . /// - public Transform Transform => Base._transform; + public override Transform Transform => Base._transform; /// /// Gets the the window is in. @@ -80,15 +88,6 @@ internal Window(BreakableWindow window, Room room) /// public ZoneType Zone => Room.Zone; - /// - /// Gets or sets the window's position. - /// - public Vector3 Position - { - get => GameObject.transform.position; - set => GameObject.transform.position = value; - } - /// /// Gets a value indicating whether or not this window is breakable. /// @@ -112,15 +111,6 @@ public float Health set => Base.health = value; } - /// - /// Gets or sets the window's rotation. - /// - public Quaternion Rotation - { - get => GameObject.transform.rotation; - set => GameObject.transform.rotation = value; - } - /// /// Gets or sets a value indicating whether or not this window can be broken by SCP. /// @@ -160,8 +150,8 @@ public static Window Get(BreakableWindow breakableWindow) => BreakableWindowToWi /// /// Gets a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. public static IEnumerable Get(Func predicate) => List.Where(predicate); /// @@ -179,8 +169,8 @@ public static bool TryGet(BreakableWindow breakableWindow, out Window window) /// /// Try-get a of filtered based on a predicate. /// - /// The condition to satify. - /// A of which contains elements that satify the condition. + /// The condition to satisfy. + /// A of which contains elements that satisfy the condition. /// Whether or not at least one window was found. public static bool TryGet(Func predicate, out IEnumerable windows) { @@ -215,7 +205,7 @@ public void DamageWindow(float amount, DamageHandlerBase handler) /// A string containing Window-related data. public override string ToString() => $"{Type} ({Health}) [{IsBroken}] *{DisableScpDamage}*"; - private GlassType GetGlassType() => Room?.Type switch + private GlassType GetGlassType() => !Room ? GlassType.Unknown : Room.Type switch { RoomType.Lcz330 => GlassType.Scp330, RoomType.LczGlassBox => GlassType.GR18, diff --git a/Exiled.API/Interfaces/IRepNotify.cs b/Exiled.API/Interfaces/IRepNotify.cs index 76d787542f..eaa2a3672a 100644 --- a/Exiled.API/Interfaces/IRepNotify.cs +++ b/Exiled.API/Interfaces/IRepNotify.cs @@ -7,7 +7,7 @@ namespace Exiled.API.Interfaces { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// Represents an interface for objects that provide replication notifications. diff --git a/Exiled.API/Structs/PrimitiveSettings.cs b/Exiled.API/Structs/PrimitiveSettings.cs index bfc8032da8..418d0c512e 100644 --- a/Exiled.API/Structs/PrimitiveSettings.cs +++ b/Exiled.API/Structs/PrimitiveSettings.cs @@ -14,6 +14,27 @@ namespace Exiled.API.Structs /// public struct PrimitiveSettings { + /// + /// Initializes a new instance of the struct. + /// + /// The type of the primitive. + /// The color of the primitive. + /// The position of the primitive. + /// The rotation of the primitive. + /// The scale of the primitive. + /// Whether or not the primitive should be spawned. + /// Whether or not the primitive should be static. + public PrimitiveSettings(PrimitiveType primitiveType, Color color, Vector3 position, Vector3 rotation, Vector3 scale, bool spawn, bool isStatic) + { + PrimitiveType = primitiveType; + Color = color; + Position = position; + Rotation = rotation; + Scale = scale; + Spawn = spawn; + IsStatic = isStatic; + } + /// /// Initializes a new instance of the struct. /// @@ -31,6 +52,7 @@ public PrimitiveSettings(PrimitiveType primitiveType, Color color, Vector3 posit Rotation = rotation; Scale = scale; Spawn = spawn; + IsStatic = false; } /// @@ -58,6 +80,11 @@ public PrimitiveSettings(PrimitiveType primitiveType, Color color, Vector3 posit /// public Vector3 Scale { get; } + /// + /// Gets a value indicating whether or not the primitive should be spawned. + /// + public bool IsStatic { get; } + /// /// Gets a value indicating whether or not the primitive should be spawned. /// diff --git a/Exiled.CustomItems/API/Extensions.cs b/Exiled.CustomItems/API/Extensions.cs deleted file mode 100644 index 07f6ed2ef0..0000000000 --- a/Exiled.CustomItems/API/Extensions.cs +++ /dev/null @@ -1,89 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.API -{ - using System; - using System.Collections.Generic; - - using Exiled.API.Features; - using Exiled.API.Features.Items; - using Exiled.CustomItems.API.Features; - - /// - /// A collection of API methods. - /// - public static class Extensions - { - /// - /// Resets the player's inventory to the provided list of items and/or customitems names, clearing any items it already possess. - /// - /// The player to which items will be given. - /// The new items that have to be added to the inventory. - /// Indicates a value whether will be called when the player receives the or not. - public static void ResetInventory(this Player player, IEnumerable newItems, bool displayMessage = false) - { - foreach (Item item in player.Items) - { - if (CustomItem.TryGet(item, out CustomItem? customItem)) - customItem?.TrackedSerials.Remove(item.Serial); - } - - player.ClearInventory(); - - foreach (string item in newItems) - { - if (Enum.TryParse(item, true, out ItemType parsedItem)) - { - player.AddItem(parsedItem); - } - else if (!CustomItem.TryGive(player, item, displayMessage)) - { - Log.Debug($"\"{item}\" is not a valid item name, nor a custom item name."); - } - } - } - - /// - /// Registers an of s. - /// - /// s to be registered. - public static void Register(this IEnumerable customItems) - { - if (customItems is null) - throw new ArgumentNullException("customItems"); - - foreach (CustomItem customItem in customItems) - customItem.TryRegister(); - } - - /// - /// Registers a . - /// - /// The to be registered. - public static void Register(this CustomItem item) => item.TryRegister(); - - /// - /// Unregisters an of s. - /// - /// s to be unregistered. - public static void Unregister(this IEnumerable customItems) - { - if (customItems is null) - throw new ArgumentNullException("customItems"); - - foreach (CustomItem customItem in customItems) - customItem.TryUnregister(); - } - - /// - /// Unregisters a . - /// - /// The to be unregistered. - public static void Unregister(this CustomItem item) => item.TryUnregister(); - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/API/Features/CustomArmor.cs b/Exiled.CustomItems/API/Features/CustomArmor.cs deleted file mode 100644 index b41051f12e..0000000000 --- a/Exiled.CustomItems/API/Features/CustomArmor.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.CustomItems.API.Features -{ - using System; - using System.ComponentModel; - - using Exiled.API.Extensions; - using Exiled.API.Features; - using Exiled.API.Features.Items; - using Exiled.Events.EventArgs.Player; - - using MEC; - - /// - /// The Custom Armor base class. - /// - public abstract class CustomArmor : CustomItem - { - /// - /// Gets or sets the to use for this armor. - /// - public override ItemType Type - { - get => base.Type; - set - { - if (!value.IsArmor() && (value != ItemType.None)) - throw new ArgumentOutOfRangeException("Type", value, "Invalid armor type."); - - base.Type = value; - } - } - - /// - /// Gets or sets how much faster stamina will drain when wearing this armor. - /// - [Description("The value must be above 1 and below 2")] - public virtual float StaminaUseMultiplier { get; set; } = 1.15f; - - /// - /// Gets or sets how strong the helmet on the armor is. - /// - [Description("The value must be above 0 and below 100")] - public virtual int HelmetEfficacy { get; set; } = 80; - - /// - /// Gets or sets how strong the vest on the armor is. - /// - [Description("The value must be above 0 and below 100")] - public virtual int VestEfficacy { get; set; } = 80; - - /// - public override void Give(Player player, bool displayMessage = true) - { - Armor armor = (Armor)Item.Create(Type); - - armor.Weight = Weight; - armor.StaminaUseMultiplier = StaminaUseMultiplier; - - armor.VestEfficacy = VestEfficacy; - armor.HelmetEfficacy = HelmetEfficacy; - - player.AddItem(armor); - - TrackedSerials.Add(armor.Serial); - - Timing.CallDelayed(0.05f, () => OnAcquired(player, armor, displayMessage)); - - if (displayMessage) - ShowPickedUpMessage(player); - } - - /// - protected override void SubscribeEvents() - { - Exiled.Events.Handlers.Player.PickingUpItem += OnInternalPickingUpItem; - base.SubscribeEvents(); - } - - /// - protected override void UnsubscribeEvents() - { - Exiled.Events.Handlers.Player.PickingUpItem -= OnInternalPickingUpItem; - base.UnsubscribeEvents(); - } - - private void OnInternalPickingUpItem(PickingUpItemEventArgs ev) - { - if (!Check(ev.Pickup) || ev.Player.Items.Count >= 8 || ev.Pickup is Exiled.API.Features.Pickups.BodyArmorPickup) - return; - - OnPickingUp(ev); - - if (!ev.IsAllowed) - return; - - ev.IsAllowed = false; - - TrackedSerials.Remove(ev.Pickup.Serial); - ev.Pickup.Destroy(); - - Give(ev.Player); - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/API/Features/CustomGrenade.cs b/Exiled.CustomItems/API/Features/CustomGrenade.cs deleted file mode 100644 index 24906352c2..0000000000 --- a/Exiled.CustomItems/API/Features/CustomGrenade.cs +++ /dev/null @@ -1,212 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.API.Features -{ - using System; - - using Exiled.API.Extensions; - using Exiled.API.Features; - using Exiled.API.Features.Items; - using Exiled.API.Features.Pickups; - using Exiled.API.Features.Pickups.Projectiles; - using Exiled.API.Features.Roles; - using Exiled.Events.EventArgs.Map; - using Exiled.Events.EventArgs.Player; - - using Footprinting; - using InventorySystem.Items; - using InventorySystem.Items.Pickups; - using InventorySystem.Items.ThrowableProjectiles; - using Mirror; - using UnityEngine; - - using Object = UnityEngine.Object; - using Server = Exiled.API.Features.Server; - - /// - /// The Custom Grenade base class. - /// - public abstract class CustomGrenade : CustomItem - { - /// - /// Gets or sets the to use for this item. - /// - public override ItemType Type - { - get => base.Type; - set - { - if (!value.IsThrowable() && value != ItemType.None) - throw new ArgumentOutOfRangeException("Type", value, "Invalid grenade type."); - - base.Type = value; - } - } - - /// - /// Gets or sets a value indicating whether gets or sets a value that determines if the grenade should explode immediately when contacting any surface. - /// - public abstract bool ExplodeOnCollision { get; set; } - - /// - /// Gets or sets a value indicating how long the grenade's fuse time should be. - /// - public abstract float FuseTime { get; set; } - - /// - /// Throw the CustomGrenade object. - /// - /// The position to throw at. - /// The amount of force to throw with. - /// The Weight of the Grenade. - /// The FuseTime of the grenade. - /// The of the grenade to spawn. - /// The to count as the thrower of the grenade. - /// The spawned. - public virtual Pickup Throw(Vector3 position, float force, float weight, float fuseTime = 3f, ItemType grenadeType = ItemType.GrenadeHE, Player? player = null) - { - if (player is null) - player = Server.Host; - - player.Role.Is(out FpcRole fpcRole); - var velocity = fpcRole.FirstPersonController.FpcModule.Motor.Velocity; - - Throwable throwable = (Throwable)Item.Create(grenadeType, player); - - ThrownProjectile thrownProjectile = Object.Instantiate(throwable.Base.Projectile, position, throwable.Owner.CameraTransform.rotation); - Transform transform = thrownProjectile.transform; - PickupSyncInfo newInfo = new() - { - ItemId = throwable.Type, - Locked = !throwable.Base._repickupable, - Serial = ItemSerialGenerator.GenerateNext(), - WeightKg = weight, - }; - if (thrownProjectile is TimeGrenade time) - time._fuseTime = fuseTime; - thrownProjectile.NetworkInfo = newInfo; - thrownProjectile.PreviousOwner = new Footprint(throwable.Owner.ReferenceHub); - NetworkServer.Spawn(thrownProjectile.gameObject); - thrownProjectile.InfoReceivedHook(default, newInfo); - if (thrownProjectile.TryGetComponent(out Rigidbody component)) - throwable.Base.PropelBody(component, throwable.Base.FullThrowSettings.StartTorque, ThrowableNetworkHandler.GetLimitedVelocity(velocity), force, throwable.Base.FullThrowSettings.UpwardsFactor); - thrownProjectile.ServerActivate(); - - return Pickup.Get(thrownProjectile); - } - - /// - /// Checks to see if the grenade is a custom grenade. - /// - /// The grenade to check. - /// True if it is a custom grenade. - public virtual bool Check(Projectile grenade) => grenade is not null && TrackedSerials.Contains(grenade.Serial); - - /// - protected override void SubscribeEvents() - { - Exiled.Events.Handlers.Player.ThrowingRequest += OnInternalThrowingRequest; - Exiled.Events.Handlers.Player.ThrownProjectile += OnInternalThrownProjectile; - Exiled.Events.Handlers.Map.ExplodingGrenade += OnInternalExplodingGrenade; - Exiled.Events.Handlers.Map.ChangedIntoGrenade += OnInternalChangedIntoGrenade; - - base.SubscribeEvents(); - } - - /// - protected override void UnsubscribeEvents() - { - Exiled.Events.Handlers.Player.ThrowingRequest -= OnInternalThrowingRequest; - Exiled.Events.Handlers.Player.ThrownProjectile -= OnInternalThrownProjectile; - Exiled.Events.Handlers.Map.ExplodingGrenade -= OnInternalExplodingGrenade; - Exiled.Events.Handlers.Map.ChangedIntoGrenade -= OnInternalChangedIntoGrenade; - - base.UnsubscribeEvents(); - } - - /// - /// Handles tracking thrown requests by custom grenades. - /// - /// . - protected virtual void OnThrowingRequest(ThrowingRequestEventArgs ev) - { - } - - /// - /// Handles tracking thrown custom grenades. - /// - /// . - protected virtual void OnThrownProjectile(ThrownProjectileEventArgs ev) - { - } - - /// - /// Handles tracking exploded custom grenades. - /// - /// . - protected virtual void OnExploding(ExplodingGrenadeEventArgs ev) - { - } - - /// - /// Handles the tracking of custom grenade pickups that are changed into live grenades by a frag grenade explosion. - /// - /// . - protected virtual void OnChangedIntoGrenade(ChangedIntoGrenadeEventArgs ev) - { - } - - private void OnInternalThrowingRequest(ThrowingRequestEventArgs ev) - { - if (!Check(ev.Player.CurrentItem)) - return; - - Log.Debug($"{ev.Player.Nickname} has thrown a {Name}!"); - - OnThrowingRequest(ev); - return; - } - - private void OnInternalThrownProjectile(ThrownProjectileEventArgs ev) - { - if (!Check(ev.Throwable)) - return; - - OnThrownProjectile(ev); - - if (ev.Projectile is TimeGrenadeProjectile timeGrenade) - timeGrenade.FuseTime = FuseTime; - - if (ExplodeOnCollision) - ev.Projectile.GameObject.AddComponent().Init((ev.Player ?? Server.Host).GameObject, ev.Projectile.Base); - } - - private void OnInternalExplodingGrenade(ExplodingGrenadeEventArgs ev) - { - if (Check(ev.Projectile)) - { - Log.Debug($"A {Name} is exploding!!"); - OnExploding(ev); - } - } - - private void OnInternalChangedIntoGrenade(ChangedIntoGrenadeEventArgs ev) - { - if (!Check(ev.Pickup)) - return; - - if (ev.Projectile is TimeGrenadeProjectile timedGrenade) - timedGrenade.FuseTime = FuseTime; - - OnChangedIntoGrenade(ev); - - if (ExplodeOnCollision) - ev.Projectile.GameObject.AddComponent().Init((ev.Pickup.PreviousOwner ?? Server.Host).GameObject, ev.Projectile.Base); - } - } -} diff --git a/Exiled.CustomItems/API/Features/CustomItem.cs b/Exiled.CustomItems/API/Features/CustomItem.cs deleted file mode 100644 index b31271dfba..0000000000 --- a/Exiled.CustomItems/API/Features/CustomItem.cs +++ /dev/null @@ -1,1153 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.API.Features -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - - using Exiled.API.Enums; - using Exiled.API.Extensions; - using Exiled.API.Features; - using Exiled.API.Features.Attributes; - using Exiled.API.Features.Pickups; - using Exiled.API.Features.Pools; - using Exiled.API.Features.Spawn; - using Exiled.API.Interfaces; - using Exiled.CustomItems.API.EventArgs; - using Exiled.Events.EventArgs.Player; - using Exiled.Events.EventArgs.Scp914; - using Exiled.Loader; - - using InventorySystem.Items.Firearms; - using InventorySystem.Items.Pickups; - - using MapGeneration.Distributors; - - using MEC; - - using PlayerRoles; - - using UnityEngine; - - using YamlDotNet.Serialization; - - using static CustomItems; - - using BaseFirearmPickup = InventorySystem.Items.Firearms.FirearmPickup; - using Firearm = Exiled.API.Features.Items.Firearm; - using Item = Exiled.API.Features.Items.Item; - using Map = Exiled.API.Features.Map; - using Player = Exiled.API.Features.Player; - using UpgradingPickupEventArgs = Exiled.Events.EventArgs.Scp914.UpgradingPickupEventArgs; - - /// - /// The Custom Item base class. - /// - public abstract class CustomItem - { - private static Dictionary typeLookupTable = new(); - private static Dictionary stringLookupTable = new(); - private static Dictionary idLookupTable = new(); - - private ItemType type = ItemType.None; - - /// - /// Gets the list of current Item Managers. - /// - public static HashSet Registered { get; } = new(); - - /// - /// Gets or sets the custom ItemID of the item. - /// - public abstract uint Id { get; set; } - - /// - /// Gets or sets the name of the item. - /// - public abstract string Name { get; set; } - - /// - /// Gets or sets the description of the item. - /// - public abstract string Description { get; set; } - - /// - /// Gets or sets the weight of the item. - /// - public abstract float Weight { get; set; } - - /// - /// Gets or sets the list of spawn locations and chances for each one. - /// - public abstract SpawnProperties SpawnProperties { get; set; } - - /// - /// Gets or sets the scale of the item. - /// - public virtual Vector3 Scale { get; set; } = Vector3.one; - - /// - /// Gets or sets the ItemType to use for this item. - /// - public virtual ItemType Type - { - get => type; - set - { - if (!Enum.IsDefined(typeof(ItemType), value)) - throw new ArgumentOutOfRangeException("Type", value, "Invalid Item type."); - - type = value; - } - } - - /// - /// Gets the list of custom items inside players' inventory being tracked as the current item. - /// - [YamlIgnore] - public HashSet TrackedSerials { get; } = new(); - - /// - /// Gets a value indicating whether or not this item causes things to happen that may be considered hacks, and thus be shown to global moderators as being present in a player's inventory when they gban them. - /// - [YamlIgnore] - public virtual bool ShouldMessageOnGban { get; } = false; - - /// - /// Gets a with a specific ID. - /// - /// The ID. - /// The matching the search, if not registered. - [Obsolete("Use Get(uint) instead.", true)] - public static CustomItem Get(int id) - { - if (!idLookupTable.ContainsKey((uint)id)) - idLookupTable.Add((uint)id, Registered.FirstOrDefault(i => i.Id == id)); - return idLookupTable[(uint)id]; - } - - /// - /// Gets a with a specific ID. - /// - /// The ID. - /// The matching the search, if not registered. - public static CustomItem Get(uint id) - { - if (!idLookupTable.ContainsKey(id)) - idLookupTable.Add(id, Registered.FirstOrDefault(i => i.Id == id)); - return idLookupTable[id]; - } - - /// - /// Gets a with a specific name. - /// - /// The name. - /// The matching the search, if not registered. - public static CustomItem Get(string name) - { - if (!stringLookupTable.ContainsKey(name)) - stringLookupTable.Add(name, Registered.FirstOrDefault(i => i.Name == name)); - return stringLookupTable[name]; - } - - /// - /// Gets a with a specific type. - /// - /// The type. - /// The matching the search, if not registered. - public static CustomItem Get(Type t) - { - if (!typeLookupTable.ContainsKey(t)) - typeLookupTable.Add(t, Registered.FirstOrDefault(i => i.GetType() == t)); - return typeLookupTable[t]; - } - - /// - /// Tries to get a with a specific ID. - /// - /// The ID to look for. - /// The found , if not registered. - /// Returns a value indicating whether the was found or not. - public static bool TryGet(uint id, out CustomItem customItem) - { - customItem = Get(id); - - return customItem is not null; - } - - /// - /// Tries to get a with a specific name. - /// - /// The name to look for. - /// The found , if not registered. - /// Returns a value indicating whether the was found or not. - public static bool TryGet(string name, out CustomItem customItem) - { - customItem = null; - if (string.IsNullOrEmpty(name)) - return false; - - customItem = uint.TryParse(name, out uint id) ? Get(id) : Get(name); - - return customItem is not null; - } - - /// - /// Tries to get a with a specific type. - /// - /// The of the item to look for. - /// The found , if not registered. - /// Returns a value indicating whether the was found or not. - public static bool TryGet(Type t, out CustomItem customItem) - { - customItem = Get(t); - - return customItem is not null; - } - - /// - /// Tries to get the player's current in their hand. - /// - /// The to check. - /// The in their hand. - /// Returns a value indicating whether the has a in their hand or not. - public static bool TryGet(Player player, out CustomItem customItem) - { - customItem = null; - if (player is null) - return false; - - customItem = Registered?.FirstOrDefault(tempCustomItem => tempCustomItem.Check(player.CurrentItem)); - - return customItem is not null; - } - - /// - /// Tries to get the player's of . - /// - /// The to check. - /// The player's of . - /// Returns a value indicating whether the has a in their hand or not. - public static bool TryGet(Player player, out IEnumerable customItems) - { - customItems = Enumerable.Empty(); - if (player is null) - return false; - - customItems = Registered.Where(tempCustomItem => player.Items.Any(tempCustomItem.Check)); - - return customItems.Any(); - } - - /// - /// Checks to see if this item is a custom item. - /// - /// The to check. - /// The this item is. - /// True if the item is a custom item. - public static bool TryGet(Item item, out CustomItem customItem) - { - customItem = item == null ? null : Registered?.FirstOrDefault(tempCustomItem => tempCustomItem.TrackedSerials.Contains(item.Serial)); - - return customItem is not null; - } - - /// - /// Checks if this pickup is a custom item. - /// - /// The to check. - /// The this pickup is. - /// True if the pickup is a custom item. - public static bool TryGet(Pickup pickup, out CustomItem customItem) - { - customItem = Registered?.FirstOrDefault(tempCustomItem => tempCustomItem.TrackedSerials.Contains(pickup.Serial)); - - return customItem is not null; - } - - /// - /// Tries to spawn a specific at a specific position. - /// - /// The ID of the to spawn. - /// The location to spawn the item. - /// The instance of the . - /// Returns a value indicating whether the was spawned or not. - public static bool TrySpawn(uint id, Vector3 position, out Pickup pickup) - { - pickup = default; - - if (!TryGet(id, out CustomItem item)) - return false; - - pickup = item?.Spawn(position); - - return true; - } - - /// - /// Tries to spawn a specific at a specific position. - /// - /// The name of the to spawn. - /// The location to spawn the item. - /// The instance of the . - /// Returns a value indicating whether the was spawned or not. - public static bool TrySpawn(string name, Vector3 position, out Pickup pickup) - { - pickup = default; - - if (!TryGet(name, out CustomItem item)) - return false; - - pickup = item?.Spawn(position, null); - - return true; - } - - /// - /// Gives to a specific a specic . - /// - /// The to give the item to. - /// The name of the to give. - /// Indicates a value whether will be called when the player receives the or not. - /// Returns a value indicating if the player was given the or not. - public static bool TryGive(Player player, string name, bool displayMessage = true) - { - if (!TryGet(name, out CustomItem item)) - return false; - - item?.Give(player, displayMessage); - - return true; - } - - /// - /// Gives to a specific a specic . - /// - /// The to give the item to. - /// The IDs of the to give. - /// Indicates a value whether will be called when the player receives the or not. - /// Returns a value indicating if the player was given the or not. - public static bool TryGive(Player player, uint id, bool displayMessage = true) - { - if (!TryGet(id, out CustomItem item)) - return false; - - item?.Give(player, displayMessage); - - return true; - } - - /// - /// Gives to a specific a specic . - /// - /// The to give the item to. - /// The of the item to give. - /// Indicates a value whether will be called when the player receives the or not. - /// Returns a value indicating if the player was given the or not. - public static bool TryGive(Player player, Type t, bool displayMessage = true) - { - if (!TryGet(t, out CustomItem item)) - return false; - - item?.Give(player, displayMessage); - - return true; - } - - /// - /// Registers all the 's present in the current assembly. - /// - /// Whether or not reflection is skipped (more efficient if you are not using your custom item classes as config objects). - /// The class to search properties for, if different from the plugin's config class. - /// A of which contains all registered 's. - public static IEnumerable RegisterItems(bool skipReflection = false, object overrideClass = null) - { - List items = new(); - Assembly assembly = Assembly.GetCallingAssembly(); - foreach (Type type in assembly.GetTypes()) - { - if ((type.BaseType != typeof(CustomItem) && !type.IsSubclassOf(typeof(CustomItem))) || type.GetCustomAttribute(typeof(CustomItemAttribute)) is null) - continue; - - foreach (Attribute attribute in type.GetCustomAttributes(typeof(CustomItemAttribute), true).Cast()) - { - CustomItem customItem = null; - bool flag = false; - - if (!skipReflection && Server.PluginAssemblies.ContainsKey(assembly)) - { - IPlugin plugin = Server.PluginAssemblies[assembly]; - foreach (PropertyInfo property in overrideClass?.GetType().GetProperties() ?? plugin.Config.GetType().GetProperties()) - { - if (property.PropertyType != type) - { - if (property.GetValue(overrideClass ?? plugin.Config) is IEnumerable enumerable) - { - List list = ListPool.Pool.Get(); - foreach (object item in enumerable) - { - if (item is CustomItem ci) - list.Add(ci); - } - - foreach (CustomItem item in list) - { - if (item.GetType() != type) - break; - - if (item.Type == ItemType.None) - item.Type = ((CustomItemAttribute)attribute).ItemType; - - if (!item.TryRegister()) - continue; - - flag = true; - items.Add(item); - } - - ListPool.Pool.Return(list); - } - - continue; - } - - customItem = property.GetValue(overrideClass ?? plugin.Config) as CustomItem; - } - } - - if (flag) - continue; - - customItem ??= (CustomItem)Activator.CreateInstance(type); - - if (customItem.Type == ItemType.None) - customItem.Type = ((CustomItemAttribute)attribute).ItemType; - - if (customItem.TryRegister()) - items.Add(customItem); - } - } - - return items; - } - - /// - /// Registers all the 's present in the current assembly. - /// - /// The of containing the target types. - /// A value indicating whether the target types should be ignored. - /// Whether or not reflection is skipped (more efficient if you are not using your custom item classes as config objects). - /// The class to search properties for, if different from the plugin's config class. - /// A of which contains all registered 's. - public static IEnumerable RegisterItems(IEnumerable targetTypes, bool isIgnored = false, bool skipReflection = false, object overrideClass = null) - { - List items = new(); - Assembly assembly = Assembly.GetCallingAssembly(); - foreach (Type type in assembly.GetTypes()) - { - if ((type.BaseType != typeof(CustomItem) && !type.IsSubclassOf(typeof(CustomItem))) || type.GetCustomAttribute(typeof(CustomItemAttribute)) is null || - (isIgnored && targetTypes.Contains(type)) || (!isIgnored && !targetTypes.Contains(type))) - continue; - - foreach (Attribute attribute in type.GetCustomAttributes(typeof(CustomItemAttribute), true).Cast()) - { - CustomItem customItem = null; - - if (!skipReflection && Server.PluginAssemblies.ContainsKey(assembly)) - { - IPlugin plugin = Server.PluginAssemblies[assembly]; - - foreach (PropertyInfo property in overrideClass?.GetType().GetProperties() ?? plugin.Config.GetType().GetProperties()) - { - if (property.PropertyType != type) - continue; - - customItem = property.GetValue(overrideClass ?? plugin.Config) as CustomItem; - break; - } - } - - customItem ??= (CustomItem)Activator.CreateInstance(type); - - if (customItem.Type == ItemType.None) - customItem.Type = ((CustomItemAttribute)attribute).ItemType; - - if (customItem.TryRegister()) - items.Add(customItem); - } - } - - return items; - } - - /// - /// Unregisters all the 's present in the current assembly. - /// - /// A of which contains all unregistered 's. - public static IEnumerable UnregisterItems() - { - List unregisteredItems = new(); - - foreach (CustomItem customItem in Registered) - { - customItem.TryUnregister(); - unregisteredItems.Add(customItem); - } - - return unregisteredItems; - } - - /// - /// Unregisters all the 's present in the current assembly. - /// - /// The of containing the target types. - /// A value indicating whether the target types should be ignored. - /// A of which contains all unregistered 's. - public static IEnumerable UnregisterItems(IEnumerable targetTypes, bool isIgnored = false) - { - List unregisteredItems = new(); - - foreach (CustomItem customItem in Registered) - { - if ((targetTypes.Contains(customItem.GetType()) && isIgnored) || (!targetTypes.Contains(customItem.GetType()) && !isIgnored)) - continue; - - customItem.TryUnregister(); - unregisteredItems.Add(customItem); - } - - return unregisteredItems; - } - - /// - /// Unregisters all the 's present in the current assembly. - /// - /// The of containing the target items. - /// A value indicating whether the target items should be ignored. - /// A of which contains all unregistered 's. - public static IEnumerable UnregisterItems(IEnumerable targetItems, bool isIgnored = false) => UnregisterItems(targetItems.Select(x => x.GetType()), isIgnored); - - /// - /// Spawns the in a specific location. - /// - /// The x coordinate. - /// The y coordinate. - /// The z coordinate. - /// The wrapper of the spawned . - public virtual Pickup Spawn(float x, float y, float z) => Spawn(new Vector3(x, y, z)); - - /// - /// Spawns a as a in a specific location. - /// - /// The x coordinate. - /// The y coordinate. - /// The z coordinate. - /// The to be spawned as a . - /// The wrapper of the spawned . - public virtual Pickup Spawn(float x, float y, float z, Item item) => Spawn(new Vector3(x, y, z), item); - - /// - /// Spawns the where a specific is, and optionally sets the previous owner. - /// - /// The position where the will be spawned. - /// The previous owner of the pickup, can be null. - /// The of the spawned . - public virtual Pickup Spawn(Player player, Player previousOwner = null) => Spawn(player.Position, previousOwner); - - /// - /// Spawns a as a where a specific is, and optionally sets the previous owner. - /// - /// The position where the will be spawned. - /// The to be spawned as a . - /// The previous owner of the pickup, can be null. - /// The of the spawned . - public virtual Pickup Spawn(Player player, Item item, Player previousOwner = null) => Spawn(player.Position, item, previousOwner); - - /// - /// Spawns the in a specific position. - /// - /// The where the will be spawned. - /// The of the item. Can be null. - /// The of the spawned . - public virtual Pickup Spawn(Vector3 position, Player previousOwner = null) => Spawn(position, Item.Create(Type), previousOwner); - - /// - /// Spawns the in a specific position. - /// - /// The where the will be spawned. - /// The to be spawned as a . - /// The of the item. Can be null. - /// The of the spawned . - public virtual Pickup Spawn(Vector3 position, Item item, Player previousOwner = null) - { - Pickup pickup = item.CreatePickup(position); - pickup.Scale = Scale; - pickup.Weight = Weight; - - if (previousOwner is not null) - pickup.PreviousOwner = previousOwner; - - TrackedSerials.Add(pickup.Serial); - - return pickup; - } - - /// - /// Spawns s inside . - /// - /// The spawn points . - /// The spawn limit. - /// Returns the number of spawned items. - public virtual uint Spawn(IEnumerable spawnPoints, uint limit) - { - uint spawned = 0; - - foreach (SpawnPoint spawnPoint in spawnPoints) - { - Log.Debug($"Attempting to spawn {Name} at {spawnPoint.Position}."); - - if (Loader.Random.NextDouble() * 100 >= spawnPoint.Chance || (limit > 0 && spawned >= limit)) - continue; - - spawned++; - - if (spawnPoint is DynamicSpawnPoint dynamicSpawnPoint && dynamicSpawnPoint.Location == SpawnLocationType.InsideLocker) - { - for (int i = 0; i < 50; i++) - { - if (Map.Lockers is null) - { - Log.Debug($"{nameof(Spawn)}: Locker list is null."); - continue; - } - - Locker locker = - Map.Lockers[ - Loader.Random.Next(Map.Lockers.Count)]; - - if (locker is null) - { - Log.Debug($"{nameof(Spawn)}: Selected locker is null."); - continue; - } - - if (locker.Loot is null) - { - Log.Debug($"{nameof(Spawn)}: Invalid locker location. Attempting to find a new one.."); - continue; - } - - if (locker.Chambers is null) - { - Log.Debug($"{nameof(Spawn)}: Locker chambers is null"); - continue; - } - - LockerChamber chamber = locker.Chambers[Loader.Random.Next(Mathf.Max(0, locker.Chambers.Length - 1))]; - - if (chamber is null) - { - Log.Debug($"{nameof(Spawn)}: chamber is null"); - continue; - } - - Vector3 position = chamber._spawnpoint.transform.position; - Spawn(position, null); - Log.Debug($"Spawned {Name} at {position} ({spawnPoint.Name})"); - - break; - } - } - else if (spawnPoint is RoleSpawnPoint roleSpawnPoint) - { - Spawn(roleSpawnPoint.Role.GetRandomSpawnLocation().Position, null); - } - else - { - Pickup pickup = Spawn(spawnPoint.Position, null); - if (pickup.Base is BaseFirearmPickup firearmPickup && this is CustomWeapon customWeapon) - { - firearmPickup.Status = new FirearmStatus(customWeapon.ClipSize, firearmPickup.Status.Flags, firearmPickup.Status.Attachments); - firearmPickup.NetworkStatus = firearmPickup.Status; - } - - Log.Debug($"Spawned {Name} at {spawnPoint.Position} ({spawnPoint.Name})"); - } - } - - return spawned; - } - - /// - /// Spawns all items at their dynamic and static positions. - /// - public virtual void SpawnAll() - { - if (SpawnProperties is null) - return; - - // This will go over each spawn property type (static, dynamic and role) to try and spawn the item. - // It will attempt to spawn in role-based locations, and then dynamic ones, and finally static. - // Math.Min is used here to ensure that our recursive Spawn() calls do not result in exceeding the spawn limit config. - // This is the same as: - // int spawned = 0; - // spawned += Spawn(SpawnProperties.RoleSpawnPoints, SpawnProperties.Limit); - // if (spawned < SpawnProperties.Limit) - // spawned += Spawn(SpawnProperties.DynamicSpawnPoints, SpawnProperties.Limit - spawned); - // if (spawned < SpawnProperties.Limit) - // Spawn(SpawnProperties.StaticSpawnPoints, SpawnProperties.Limit - spawned); - Spawn(SpawnProperties.StaticSpawnPoints, Math.Min(0, SpawnProperties.Limit - Math.Min(0, Spawn(SpawnProperties.DynamicSpawnPoints, SpawnProperties.Limit) - Spawn(SpawnProperties.RoleSpawnPoints, SpawnProperties.Limit)))); - } - - /// - /// Gives an as a to a . - /// - /// The who will receive the item. - /// The to be given. - /// Indicates whether or not will be called when the player receives the item. - public virtual void Give(Player player, Item item, bool displayMessage = true) - { - try - { - Log.Debug($"{Name}.{nameof(Give)}: Item Serial: {item.Serial} Ammo: {(item is Firearm firearm ? firearm.Ammo : -1)}"); - - player.AddItem(item); - - Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker."); - if (!TrackedSerials.Contains(item.Serial)) - TrackedSerials.Add(item.Serial); - - Timing.CallDelayed(0.05f, () => OnAcquired(player, item, displayMessage)); - } - catch (Exception e) - { - Log.Error($"{nameof(Give)}: {e}"); - } - } - - /// - /// Gives a as a to a . - /// - /// The who will receive the item. - /// The to be given. - /// Indicates whether or not will be called when the player receives the item. - public virtual void Give(Player player, Pickup pickup, bool displayMessage = true) => Give(player, player.AddItem(pickup), displayMessage); - - /// - /// Gives the to a player. - /// - /// The who will receive the item. - /// Indicates whether or not will be called when the player receives the item. - public virtual void Give(Player player, bool displayMessage = true) => Give(player, Item.Create(Type), displayMessage); - - /// - /// Called when the item is registered. - /// - public virtual void Init() - { - typeLookupTable.Add(GetType(), this); - stringLookupTable.Add(Name, this); - idLookupTable.Add(Id, this); - - SubscribeEvents(); - } - - /// - /// Called when the item is unregistered. - /// - public virtual void Destroy() - { - UnsubscribeEvents(); - - typeLookupTable.Remove(GetType()); - stringLookupTable.Remove(Name); - idLookupTable.Remove(Id); - } - - /// - /// Checks the specified pickup to see if it is a custom item. - /// - /// The to check. - /// True if it is a custom item. - public virtual bool Check(Pickup pickup) => pickup is not null && TrackedSerials.Contains(pickup.Serial); - - /// - /// Checks the specified inventory item to see if it is a custom item. - /// - /// The to check. - /// True if it is a custom item. - public virtual bool Check(Item item) => item is not null && TrackedSerials.Contains(item.Serial); - - /// - /// Checks the specified player's current item to see if it is a custom item. - /// - /// The who's current item should be checked. - /// True if it is a custom item. - public virtual bool Check(Player player) => Check(player?.CurrentItem); - - /// - public override string ToString() => $"[{Name} ({Type}) | {Id}] {Description}"; - - /// - /// Registers a . - /// - /// Returns a value indicating whether the was registered or not. - internal bool TryRegister() - { - if (!Instance?.Config.IsEnabled ?? false) - return false; - - Log.Debug($"Trying to register {Name} ({Id})."); - if (!Registered.Contains(this)) - { - Log.Debug("Registered items doesn't contain this item yet.."); - if (Registered.Any(customItem => customItem.Id == Id)) - { - Log.Warn($"{Name} has tried to register with the same custom item ID as another item: {Id}. It will not be registered."); - - return false; - } - - Log.Debug("Adding item to registered list.."); - Registered.Add(this); - - Init(); - - Log.Debug($"{Name} ({Id}) [{Type}] has been successfully registered."); - - return true; - } - - Log.Warn($"Couldn't register {Name} ({Id}) [{Type}] as it already exists."); - - return false; - } - - /// - /// Tries to unregister a . - /// - /// Returns a value indicating whether the was unregistered or not. - internal bool TryUnregister() - { - Destroy(); - - if (!Registered.Remove(this)) - { - Log.Warn($"Cannot unregister {Name} ({Id}) [{Type}], it hasn't been registered yet."); - - return false; - } - - return true; - } - - /// - /// Called after the manager is initialized, to allow loading of special event handlers. - /// - protected virtual void SubscribeEvents() - { - Exiled.Events.Handlers.Player.Dying += OnInternalOwnerDying; - Exiled.Events.Handlers.Player.DroppingItem += OnInternalDropping; - Exiled.Events.Handlers.Player.ChangingItem += OnInternalChanging; - Exiled.Events.Handlers.Player.Escaping += OnInternalOwnerEscaping; - Exiled.Events.Handlers.Player.PickingUpItem += OnInternalPickingUp; - Exiled.Events.Handlers.Player.ItemAdded += OnInternalItemAdded; - Exiled.Events.Handlers.Scp914.UpgradingPickup += OnInternalUpgradingPickup; - Exiled.Events.Handlers.Server.WaitingForPlayers += OnWaitingForPlayers; - Exiled.Events.Handlers.Player.Handcuffing += OnInternalOwnerHandcuffing; - Exiled.Events.Handlers.Player.ChangingRole += OnInternalOwnerChangingRole; - Exiled.Events.Handlers.Scp914.UpgradingInventoryItem += OnInternalUpgradingInventoryItem; - } - - /// - /// Called when the manager is being destroyed, to allow unloading of special event handlers. - /// - protected virtual void UnsubscribeEvents() - { - Exiled.Events.Handlers.Player.Dying -= OnInternalOwnerDying; - Exiled.Events.Handlers.Player.DroppingItem -= OnInternalDropping; - Exiled.Events.Handlers.Player.ChangingItem -= OnInternalChanging; - Exiled.Events.Handlers.Player.Escaping -= OnInternalOwnerEscaping; - Exiled.Events.Handlers.Player.PickingUpItem -= OnInternalPickingUp; - Exiled.Events.Handlers.Player.ItemAdded -= OnInternalItemAdded; - Exiled.Events.Handlers.Scp914.UpgradingPickup -= OnInternalUpgradingPickup; - Exiled.Events.Handlers.Server.WaitingForPlayers -= OnWaitingForPlayers; - Exiled.Events.Handlers.Player.Handcuffing -= OnInternalOwnerHandcuffing; - Exiled.Events.Handlers.Player.ChangingRole -= OnInternalOwnerChangingRole; - Exiled.Events.Handlers.Scp914.UpgradingInventoryItem -= OnInternalUpgradingInventoryItem; - } - - /// - /// Handles tracking items when they a player changes their role. - /// - /// . - protected virtual void OnOwnerChangingRole(OwnerChangingRoleEventArgs ev) - { - } - - /// - /// Handles making sure custom items are not "lost" when a player dies. - /// - /// . - protected virtual void OnOwnerDying(OwnerDyingEventArgs ev) - { - } - - /// - /// Handles making sure custom items are not "lost" when a player escapes. - /// - /// . - protected virtual void OnOwnerEscaping(OwnerEscapingEventArgs ev) - { - } - - /// - /// Handles making sure custom items are not "lost" when being handcuffed. - /// - /// . - protected virtual void OnOwnerHandcuffing(OwnerHandcuffingEventArgs ev) - { - } - - /// - /// Handles tracking items when they are dropped by a player. - /// - /// . - protected virtual void OnDropping(DroppingItemEventArgs ev) - { - } - - /// - /// Handles tracking items when they are picked up by a player. - /// - /// . - protected virtual void OnPickingUp(PickingUpItemEventArgs ev) - { - } - - /// - /// Handles tracking items when they are selected in the player's inventory. - /// - /// . - protected virtual void OnChanging(ChangingItemEventArgs ev) => ShowSelectedMessage(ev.Player); - - /// - /// Handles making sure custom items are not affected by SCP-914. - /// - /// . - protected virtual void OnUpgrading(UpgradingEventArgs ev) - { - } - - /// - protected virtual void OnUpgrading(UpgradingItemEventArgs ev) - { - } - - /// - /// Called anytime the item enters a player's inventory by any means. - /// - /// The acquiring the item. - /// The being acquired. - /// Whether or not the Pickup hint should be displayed. - protected virtual void OnAcquired(Player player, Item item, bool displayMessage) - { - if (displayMessage) - ShowPickedUpMessage(player); - } - - /// - /// Clears the lists of item uniqIDs and Pickups since any still in the list will be invalid. - /// - protected virtual void OnWaitingForPlayers() - { - TrackedSerials.Clear(); - } - - /// - /// Shows a message to the player upon picking up a custom item. - /// - /// The who will be shown the message. - protected virtual void ShowPickedUpMessage(Player player) - { - if (Instance!.Config.PickedUpHint.Show) - player.ShowHint(string.Format(Instance.Config.PickedUpHint.Content, Name, Description), Instance.Config.PickedUpHint.Duration); - } - - /// - /// Shows a message to the player upon selecting a custom item. - /// - /// The who will be shown the message. - protected virtual void ShowSelectedMessage(Player player) - { - if (Instance!.Config.SelectedHint.Show) - player.ShowHint(string.Format(Instance.Config.SelectedHint.Content, Name, Description), Instance.Config.SelectedHint.Duration); - } - - private void OnInternalOwnerChangingRole(ChangingRoleEventArgs ev) - { - if (ev.Reason == SpawnReason.Escaped) - return; - - foreach (Item item in ev.Player.Items.ToList()) - { - if (!Check(item)) - continue; - - OnOwnerChangingRole(new OwnerChangingRoleEventArgs(item.Base, ev)); - - TrackedSerials.Remove(item.Serial); - - ev.Player.RemoveItem(item); - - Spawn(ev.Player, item, ev.Player); - } - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); - } - - private void OnInternalOwnerDying(DyingEventArgs ev) - { - foreach (Item item in ev.Player.Items.ToList()) - { - if (!Check(item)) - continue; - - OnOwnerDying(new OwnerDyingEventArgs(item, ev)); - - if (!ev.IsAllowed) - continue; - - ev.Player.RemoveItem(item); - - TrackedSerials.Remove(item.Serial); - - Spawn(ev.Player, item, ev.Player); - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); - } - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); - } - - private void OnInternalOwnerEscaping(EscapingEventArgs ev) - { - foreach (Item item in ev.Player.Items.ToList()) - { - if (!Check(item)) - continue; - - OnOwnerEscaping(new OwnerEscapingEventArgs(item, ev)); - - if (!ev.IsAllowed) - continue; - - ev.Player.RemoveItem(item); - - TrackedSerials.Remove(item.Serial); - - Timing.CallDelayed(1.5f, () => Spawn(ev.Player.Position, item, null)); - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); - } - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); - } - - private void OnInternalOwnerHandcuffing(HandcuffingEventArgs ev) - { - foreach (Item item in ev.Target.Items.ToList()) - { - if (!Check(item)) - continue; - - OnOwnerHandcuffing(new OwnerHandcuffingEventArgs(item, ev)); - - if (!ev.IsAllowed) - continue; - - ev.Target.RemoveItem(item); - - TrackedSerials.Remove(item.Serial); - - Spawn(ev.Target, item, ev.Target); - } - } - - private void OnInternalDropping(DroppingItemEventArgs ev) - { - if (!Check(ev.Item)) - return; - - OnDropping(ev); - } - - private void OnInternalPickingUp(PickingUpItemEventArgs ev) - { - if (!Check(ev.Pickup) || ev.Player.Items.Count >= 8) - return; - - OnPickingUp(ev); - - if (!ev.IsAllowed) - return; - } - - private void OnInternalItemAdded(ItemAddedEventArgs ev) - { - if (!Check(ev.Pickup)) - return; - - OnAcquired(ev.Player, ev.Item, true); - } - - private void OnInternalChanging(ChangingItemEventArgs ev) - { - if (!Check(ev.Item)) - { - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_displayName)); - return; - } - - if (ShouldMessageOnGban) - { - foreach (Player player in Player.Get(RoleTypeId.Spectator)) - Timing.CallDelayed(0.5f, () => player.SendFakeSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_displayName), $"{ev.Player.Nickname} (CustomItem: {Name})")); - } - - OnChanging(ev); - } - - private void OnInternalUpgradingInventoryItem(UpgradingInventoryItemEventArgs ev) - { - if (!Check(ev.Item)) - return; - - ev.IsAllowed = false; - - OnUpgrading(new UpgradingItemEventArgs(ev.Player, ev.Item.Base, ev.KnobSetting)); - } - - private void OnInternalUpgradingPickup(UpgradingPickupEventArgs ev) - { - if (!Check(ev.Pickup)) - return; - - ev.IsAllowed = false; - - Timing.CallDelayed(3.5f, () => - { - ev.Pickup.Position = ev.OutputPosition; - OnUpgrading(new UpgradingEventArgs(ev.Pickup.Base, ev.OutputPosition, ev.KnobSetting)); - }); - } - } -} diff --git a/Exiled.CustomItems/API/Features/CustomWeapon.cs b/Exiled.CustomItems/API/Features/CustomWeapon.cs deleted file mode 100644 index 7b3ce94054..0000000000 --- a/Exiled.CustomItems/API/Features/CustomWeapon.cs +++ /dev/null @@ -1,283 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.API.Features -{ - using System; - - using Exiled.API.Extensions; - using Exiled.API.Features; - using Exiled.API.Features.DamageHandlers; - using Exiled.API.Features.Items; - using Exiled.API.Features.Pickups; - using Exiled.Events.EventArgs.Player; - - using InventorySystem.Items.Firearms.Attachments; - using InventorySystem.Items.Firearms.Attachments.Components; - - using UnityEngine; - - using Firearm = Exiled.API.Features.Items.Firearm; - using Player = Exiled.API.Features.Player; - - /// - /// The Custom Weapon base class. - /// - public abstract class CustomWeapon : CustomItem - { - /// - /// Gets or sets value indicating what s the weapon will have. - /// - public virtual AttachmentName[] Attachments { get; set; } = { }; - - /// - public override ItemType Type - { - get => base.Type; - set - { - if (!value.IsWeapon(false) && value != ItemType.None) - throw new ArgumentOutOfRangeException($"{nameof(Type)}", value, "Invalid weapon type."); - - base.Type = value; - } - } - - /// - /// Gets or sets the weapon damage. - /// - public abstract float Damage { get; set; } - - /// - /// Gets or sets a value indicating how big of a clip the weapon will have. - /// - public virtual byte ClipSize { get; set; } - - /// - /// Gets or sets a value indicating whether or not to allow friendly fire with this weapon on FF-enabled servers. - /// - public virtual bool FriendlyFire { get; set; } - - /// - public override Pickup? Spawn(Vector3 position, Player? previousOwner = null) - { - if (Item.Create(Type) is not Firearm firearm) - { - Log.Debug($"{nameof(Spawn)}: Item is not Firearm."); - return null; - } - - if (!Attachments.IsEmpty()) - firearm.AddAttachment(Attachments); - - firearm.Ammo = ClipSize; - firearm.MaxAmmo = ClipSize; - - Pickup? pickup = firearm.CreatePickup(position); - - if (pickup is null) - { - Log.Debug($"{nameof(Spawn)}: Pickup is null."); - return null; - } - - pickup.Weight = Weight; - pickup.Scale = Scale; - if (previousOwner is not null) - pickup.PreviousOwner = previousOwner; - - TrackedSerials.Add(pickup.Serial); - return pickup; - } - - /// - public override Pickup? Spawn(Vector3 position, Item item, Player? previousOwner = null) - { - if (item is Firearm firearm) - { - if (!Attachments.IsEmpty()) - firearm.AddAttachment(Attachments); - - byte ammo = firearm.Ammo; - firearm.MaxAmmo = ClipSize; - Log.Debug($"{nameof(Name)}.{nameof(Spawn)}: Spawning weapon with {ammo} ammo."); - Pickup? pickup = firearm.CreatePickup(position); - pickup.Scale = Scale; - - if (previousOwner is not null) - pickup.PreviousOwner = previousOwner; - - TrackedSerials.Add(pickup.Serial); - return pickup; - } - - return base.Spawn(position, item, previousOwner); - } - - /// - public override void Give(Player player, bool displayMessage = true) - { - Item item = player.AddItem(Type); - - if (item is Firearm firearm) - { - if (!Attachments.IsEmpty()) - firearm.AddAttachment(Attachments); - - firearm.Ammo = ClipSize; - firearm.MaxAmmo = ClipSize; - } - - Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker."); - TrackedSerials.Add(item.Serial); - - OnAcquired(player, item, displayMessage); - } - - /// - protected override void SubscribeEvents() - { - Exiled.Events.Handlers.Player.ReloadingWeapon += OnInternalReloading; - Exiled.Events.Handlers.Player.Shooting += OnInternalShooting; - Exiled.Events.Handlers.Player.Shot += OnInternalShot; - Exiled.Events.Handlers.Player.Hurting += OnInternalHurting; - - base.SubscribeEvents(); - } - - /// - protected override void UnsubscribeEvents() - { - Exiled.Events.Handlers.Player.ReloadingWeapon -= OnInternalReloading; - Exiled.Events.Handlers.Player.Shooting -= OnInternalShooting; - Exiled.Events.Handlers.Player.Shot -= OnInternalShot; - Exiled.Events.Handlers.Player.Hurting -= OnInternalHurting; - - base.UnsubscribeEvents(); - } - - /// - /// Handles reloading for custom weapons. - /// - /// . - protected virtual void OnReloading(ReloadingWeaponEventArgs ev) - { - } - - /// - /// Handles shooting for custom weapons. - /// - /// . - protected virtual void OnShooting(ShootingEventArgs ev) - { - } - - /// - /// Handles shot for custom weapons. - /// - /// . - protected virtual void OnShot(ShotEventArgs ev) - { - } - - /// - /// Handles hurting for custom weapons. - /// - /// . - protected virtual void OnHurting(HurtingEventArgs ev) - { - if (ev.IsAllowed && Damage > 0f) - ev.Amount = Damage; - } - - private void OnInternalReloading(ReloadingWeaponEventArgs ev) - { - if (!Check(ev.Firearm)) - return; - - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: Reloading weapon. Calling external reload event.."); - OnReloading(ev); - - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: External event ended. {ev.IsAllowed}"); - if (!ev.IsAllowed) - { - Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: External event turned is allowed to false, returning."); - return; - } - - ev.Firearm.MaxAmmo = ClipSize; - } - - private void OnInternalShooting(ShootingEventArgs ev) - { - if (!Check(ev.Player.CurrentItem)) - return; - - OnShooting(ev); - } - - private void OnInternalShot(ShotEventArgs ev) - { - if (!Check(ev.Player.CurrentItem)) - return; - - OnShot(ev); - } - - private void OnInternalHurting(HurtingEventArgs ev) - { - if (ev.Attacker is null) - { - return; - } - - if (ev.Player is null) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: target null"); - return; - } - - if (!Check(ev.Attacker.CurrentItem)) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: !Check()"); - return; - } - - if (ev.Attacker == ev.Player) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: attacker == target"); - return; - } - - if (ev.DamageHandler is null) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: Handler null"); - return; - } - - if (!ev.DamageHandler.CustomBase.BaseIs(out FirearmDamageHandler firearmDamageHandler)) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: Handler not firearm"); - return; - } - - if (!Check(firearmDamageHandler.Item)) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: type != type"); - return; - } - - if (!FriendlyFire && (ev.Attacker.Role.Team == ev.Player.Role.Team)) - { - Log.Debug($"{Name}: {nameof(OnInternalHurting)}: FF is disabled for this weapon!"); - return; - } - - OnHurting(ev); - } - } -} diff --git a/Exiled.CustomItems/Commands/Give.cs b/Exiled.CustomItems/Commands/Give.cs deleted file mode 100644 index cf228ca56c..0000000000 --- a/Exiled.CustomItems/Commands/Give.cs +++ /dev/null @@ -1,123 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands -{ - using System; - using System.Collections.Generic; - using System.Linq; - - using CommandSystem; - - using Exiled.API.Features; - using Exiled.CustomItems.API.Features; - using Exiled.Permissions.Extensions; - - using RemoteAdmin; - - /// - /// The command to give a player an item. - /// - internal sealed class Give : ICommand - { - private Give() - { - } - - /// - /// Gets the instance. - /// - public static Give Instance { get; } = new(); - - /// - public string Command { get; } = "give"; - - /// - public string[] Aliases { get; } = { "g" }; - - /// - public string Description { get; } = "Gives a custom item."; - - /// - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - if (!sender.CheckPermission("customitems.give")) - { - response = "Permission Denied, required: customitems.give"; - return false; - } - - if (arguments.Count == 0) - { - response = "give [Nickname/PlayerID/UserID/all/*]"; - return false; - } - - if (!CustomItem.TryGet(arguments.At(0), out CustomItem? item)) - { - response = $"Custom item {arguments.At(0)} not found!"; - return false; - } - - if (arguments.Count == 1) - { - if (sender is PlayerCommandSender playerCommandSender) - { - Player player = Player.Get(playerCommandSender.SenderId); - - if (!CheckEligible(player)) - { - response = "You cannot receive custom items!"; - return false; - } - - item?.Give(player); - response = $"{item?.Name} given to {player.Nickname} ({player.UserId})"; - return true; - } - - response = "Failed to provide a valid player, please follow the syntax."; - return false; - } - - string identifier = string.Join(" ", arguments.Skip(1)); - - switch (identifier) - { - case "*": - case "all": - List eligiblePlayers = Player.List.Where(CheckEligible).ToList(); - foreach (Player ply in eligiblePlayers) - item?.Give(ply); - - response = $"Custom item {item?.Name} given to all players who can receive them ({eligiblePlayers.Count} players)"; - return true; - default: - if (Player.Get(identifier) is not { } player) - { - response = $"Unable to find player: {identifier}."; - return false; - } - - if (!CheckEligible(player)) - { - response = "Player cannot receive custom items!"; - return false; - } - - item?.Give(player); - response = $"{item?.Name} given to {player.Nickname} ({player.UserId})"; - return true; - } - } - - /// - /// Checks if the player is eligible to receive custom items. - /// - private bool CheckEligible(Player player) => player.IsAlive && !player.IsCuffed && (player.Items.Count < 8); - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Commands/Info.cs b/Exiled.CustomItems/Commands/Info.cs deleted file mode 100644 index f0de9f1a99..0000000000 --- a/Exiled.CustomItems/Commands/Info.cs +++ /dev/null @@ -1,83 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands -{ - using System; - using System.Text; - - using CommandSystem; - - using Exiled.API.Features.Pools; - using Exiled.API.Features.Spawn; - using Exiled.CustomItems.API.Features; - using Exiled.Permissions.Extensions; - - /// - /// The command to view info about a specific item. - /// - internal sealed class Info : ICommand - { - private Info() - { - } - - /// - /// Gets the instance. - /// - public static Info Instance { get; } = new(); - - /// - public string Command { get; } = "info"; - - /// - public string[] Aliases { get; } = { "i" }; - - /// - public string Description { get; } = "Gets more information about the specified custom item."; - - /// - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - if (!sender.CheckPermission("customitems.info")) - { - response = "Permission Denied, required: customitems.info"; - return false; - } - - if (arguments.Count < 1) - { - response = "info [Custom item name/Custom item ID]"; - return false; - } - - if (!(uint.TryParse(arguments.At(0), out uint id) && CustomItem.TryGet(id, out CustomItem? item)) && - !CustomItem.TryGet(arguments.At(0), out item)) - { - response = $"{arguments.At(0)} is not a valid custom item."; - return false; - } - - StringBuilder message = StringBuilderPool.Pool.Get().AppendLine(); - - message.Append("- ").Append(item?.Name).Append(" (").Append(item?.Id).AppendLine(")") - .Append("- ").AppendLine(item?.Description) - .AppendLine(item?.Type.ToString()) - .Append("- Spawn Limit: ").AppendLine(item?.SpawnProperties?.Limit.ToString()).AppendLine() - .Append("[Spawn Locations (").Append(item?.SpawnProperties?.DynamicSpawnPoints.Count + item?.SpawnProperties?.StaticSpawnPoints.Count).AppendLine(")]"); - - foreach (DynamicSpawnPoint spawnPoint in item?.SpawnProperties?.DynamicSpawnPoints!) - message.Append(spawnPoint.Name).Append(' ').Append(spawnPoint.Position).Append(" Chance: ").Append(spawnPoint.Chance).AppendLine("%"); - - foreach (StaticSpawnPoint spawnPoint in item.SpawnProperties.StaticSpawnPoints) - message.Append(spawnPoint.Name).Append(' ').Append(spawnPoint.Position).Append(" Chance: ").Append(spawnPoint.Chance).AppendLine("%"); - - response = StringBuilderPool.Pool.ToStringReturn(message); - return true; - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Commands/List/List.cs b/Exiled.CustomItems/Commands/List/List.cs deleted file mode 100644 index 9ee1bcf628..0000000000 --- a/Exiled.CustomItems/Commands/List/List.cs +++ /dev/null @@ -1,52 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands.List -{ - using System; - - using CommandSystem; - - /// - /// The command to list all installed items. - /// - internal sealed class List : ParentCommand - { - private List() - { - LoadGeneratedCommands(); - } - - /// - /// Gets the instance. - /// - public static List Instance { get; } = new(); - - /// - public override string Command { get; } = "list"; - - /// - public override string[] Aliases { get; } = { "s", "l", "show", "sh" }; - - /// - public override string Description { get; } = "Gets a list of all currently registered custom items."; - - /// - public override void LoadGeneratedCommands() - { - RegisterCommand(Registered.Instance); - RegisterCommand(Tracked.Instance); - } - - /// - protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) - { - response = $"Invalid subcommand! Available: registered, insideinventories"; - return false; - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Commands/List/Registered.cs b/Exiled.CustomItems/Commands/List/Registered.cs deleted file mode 100644 index d10bc0f0e9..0000000000 --- a/Exiled.CustomItems/Commands/List/Registered.cs +++ /dev/null @@ -1,73 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands.List -{ - using System; - using System.Linq; - using System.Text; - - using CommandSystem; - - using Exiled.API.Features.Pools; - using Exiled.CustomItems.API.Features; - using Exiled.Permissions.Extensions; - - /// - internal sealed class Registered : ICommand - { - private Registered() - { - } - - /// - /// Gets the command instance. - /// - public static Registered Instance { get; } = new(); - - /// - public string Command { get; } = "registered"; - - /// - public string[] Aliases { get; } = { "r", "reg" }; - - /// - public string Description { get; } = "Gets a list of registered custom items."; - - /// - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - if (!sender.CheckPermission("customitems.list.registered")) - { - response = "Permission Denied, required: customitems.list.registered"; - return false; - } - - if (arguments.Count != 0) - { - response = "list registered"; - return false; - } - - if (CustomItem.Registered.Count == 0) - { - response = "There are no custom items currently on this server."; - return false; - } - - StringBuilder message = StringBuilderPool.Pool.Get().AppendLine(); - - message.Append("[Registered custom items (").Append(CustomItem.Registered.Count).AppendLine(")]"); - - foreach (CustomItem customItem in CustomItem.Registered.OrderBy(item => item.Id)) - message.Append('[').Append(customItem.Id).Append(". ").Append(customItem.Name).Append(" (").Append(customItem.Type).Append(')').AppendLine("]"); - - response = StringBuilderPool.Pool.ToStringReturn(message); - return true; - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Commands/List/Tracked.cs b/Exiled.CustomItems/Commands/List/Tracked.cs deleted file mode 100644 index ed826f463e..0000000000 --- a/Exiled.CustomItems/Commands/List/Tracked.cs +++ /dev/null @@ -1,96 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands.List -{ - using System; - using System.Linq; - using System.Text; - - using CommandSystem; - - using Exiled.API.Features; - using Exiled.API.Features.Pools; - using Exiled.CustomItems.API.Features; - using Exiled.Permissions.Extensions; - - using RemoteAdmin; - - /// - internal sealed class Tracked : ICommand - { - private Tracked() - { - } - - /// - /// Gets the command instance. - /// - public static Tracked Instance { get; } = new(); - - /// - public string Command { get; } = "insideinventories"; - - /// - public string[] Aliases { get; } = { "ii", "inside", "inv", "inventories" }; - - /// - public string Description { get; } = "Gets a list of custom items actually inside of players' inventories."; - - /// - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - if (!sender.CheckPermission("customitems.list.insideinventories") && sender is PlayerCommandSender playerSender && !playerSender.FullPermissions) - { - response = "Permission Denied, required: customitems.list.insideinventories"; - return false; - } - - if (arguments.Count != 0) - { - response = "list insideinventories"; - return false; - } - - StringBuilder message = StringBuilderPool.Pool.Get(); - - int count = 0; - - foreach (CustomItem customItem in CustomItem.Registered) - { - if (customItem.TrackedSerials.Count == 0) - continue; - - message.AppendLine() - .Append('[').Append(customItem.Id).Append(". ").Append(customItem.Name).Append(" (").Append(customItem.Type).Append(')') - .Append(" {").Append(customItem.TrackedSerials.Count).AppendLine("}]").AppendLine(); - - count += customItem.TrackedSerials.Count; - - foreach (int insideInventory in customItem.TrackedSerials) - { - Player owner = Player.List.FirstOrDefault(player => player.Inventory.UserInventory.Items.Any(item => item.Key == insideInventory)); - - message.Append(insideInventory).Append(". "); - - if (owner is null) - message.AppendLine("Nobody"); - else - message.Append(owner.Nickname).Append(" (").Append(owner.UserId).Append(") (").Append(owner.Id).Append(") [").Append(owner.Role).AppendLine("]"); - } - } - - if (message.Length == 0) - message.Append("There are no custom items inside inventories."); - else - message.Insert(0, Environment.NewLine + "[Custom items inside inventories (" + count + ")]" + Environment.NewLine); - - response = StringBuilderPool.Pool.ToStringReturn(message); - return true; - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Commands/Main.cs b/Exiled.CustomItems/Commands/Main.cs deleted file mode 100644 index 46c9573d49..0000000000 --- a/Exiled.CustomItems/Commands/Main.cs +++ /dev/null @@ -1,54 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands -{ - using System; - - using CommandSystem; - - /// - /// The main command. - /// - [CommandHandler(typeof(RemoteAdminCommandHandler))] - [CommandHandler(typeof(GameConsoleCommandHandler))] - internal sealed class Main : ParentCommand - { - /// - /// Initializes a new instance of the class. - /// - public Main() - { - LoadGeneratedCommands(); - } - - /// - public override string Command { get; } = "customitems"; - - /// - public override string[] Aliases { get; } = { "ci", "cis" }; - - /// - public override string Description { get; } = string.Empty; - - /// - public override void LoadGeneratedCommands() - { - RegisterCommand(Give.Instance); - RegisterCommand(Spawn.Instance); - RegisterCommand(Info.Instance); - RegisterCommand(List.List.Instance); - } - - /// - protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) - { - response = "Invalid subcommand! Available: give, spawn, info, list"; - return false; - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Commands/Spawn.cs b/Exiled.CustomItems/Commands/Spawn.cs deleted file mode 100644 index 99f438e436..0000000000 --- a/Exiled.CustomItems/Commands/Spawn.cs +++ /dev/null @@ -1,107 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems.Commands -{ - using System; - - using CommandSystem; - - using Exiled.API.Enums; - using Exiled.API.Extensions; - using Exiled.API.Features; - using Exiled.CustomItems.API.Features; - using Exiled.Permissions.Extensions; - - using UnityEngine; - - /// - /// The command to spawn a specific item. - /// - internal sealed class Spawn : ICommand - { - private Spawn() - { - } - - /// - /// Gets the instance. - /// - public static Spawn Instance { get; } = new(); - - /// - public string Command { get; } = "spawn"; - - /// - public string[] Aliases { get; } = { "sp" }; - - /// - public string Description { get; } = "Spawn an item at the specified Spawn Location, coordinates, or at the designated player's feet."; - - /// - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - if (!sender.CheckPermission("customitems.spawn")) - { - response = "Permission Denied, required: customitems.spawn"; - return false; - } - - if (arguments.Count < 2) - { - response = "spawn [Custom item name] [Location name]\nspawn [Custom item name] [Nickname/PlayerID/UserID]\nspawn [Custom item name] [X] [Y] [Z]"; - return false; - } - - if (!CustomItem.TryGet(arguments.At(0), out CustomItem? item)) - { - response = $" {arguments.At(0)} is not a valid custom item."; - return false; - } - - Vector3 position; - - if (Enum.TryParse(arguments.At(1), out SpawnLocationType location)) - { - position = location.GetPosition(); - } - else - { - if (Player.Get(arguments.At(1)) is Player player) - { - if (player.IsDead) - { - response = $"Cannot spawn custom items under dead players!"; - return false; - } - - position = player.Position; - } - else if (arguments.Count > 3) - { - if (!float.TryParse(arguments.At(1), out float x) || !float.TryParse(arguments.At(2), out float y) || !float.TryParse(arguments.At(3), out float z)) - { - response = "Invalid coordinates selected."; - return false; - } - - position = new Vector3(x, y, z); - } - else - { - response = $"Unable to find spawn location: {arguments.At(1)}."; - return false; - } - } - - item?.Spawn(position); - - response = $"{item?.Name} ({item?.Type}) has been spawned at {position}."; - return true; - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Config.cs b/Exiled.CustomItems/Config.cs deleted file mode 100644 index ffb59c2746..0000000000 --- a/Exiled.CustomItems/Config.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems -{ - using System.ComponentModel; - - using Exiled.API.Features; - using Exiled.API.Interfaces; - using Exiled.CustomItems.API.Features; - - /// - /// The plugin's config class. - /// - public class Config : IConfig - { - /// - [Description("Indicates whether this plugin is enabled or not.")] - public bool IsEnabled { get; set; } = true; - - /// - public bool Debug { get; set; } = false; - - /// - /// Gets the hint that is shown when someone pickups a . - /// - [Description("The hint that is shown when someone pickups a custom item.")] - public Broadcast PickedUpHint { get; private set; } = new("You have picked up a {0}\n{1}"); - - /// - /// Gets the hint that is shown when someone pickups a . - /// - [Description("The hint that is shown when someone selects a custom item.")] - public Broadcast SelectedHint { get; private set; } = new("You have selected a {0}\n{1}", 5); - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/CustomItems.cs b/Exiled.CustomItems/CustomItems.cs deleted file mode 100644 index 8a8e15b368..0000000000 --- a/Exiled.CustomItems/CustomItems.cs +++ /dev/null @@ -1,62 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomItems -{ - using System; - - using Exiled.API.Features; - using Exiled.CustomItems.Events; - - using HarmonyLib; - - /// - /// Handles all CustomItem API. - /// - public class CustomItems : Plugin - { - private RoundHandler? roundHandler; - private PlayerHandler? playerHandler; - private Harmony? harmony; - - /// - /// Gets the static reference to this class. - /// - public static CustomItems? Instance { get; private set; } - - /// - public override void OnEnabled() - { - Instance = this; - roundHandler = new RoundHandler(); - playerHandler = new PlayerHandler(); - - Exiled.Events.Handlers.Server.RoundStarted += roundHandler.OnRoundStarted; - - Exiled.Events.Handlers.Player.ChangingItem += playerHandler.OnChangingItem; - - harmony = new Harmony($"com.{nameof(CustomItems)}.ExiledTeam-{DateTime.Now.Ticks}"); - GlobalPatchProcessor.PatchAll(harmony, out int failedPatch); - if (failedPatch != 0) - Log.Error($"Patching failed! There are {failedPatch} broken patches."); - - base.OnEnabled(); - } - - /// - public override void OnDisabled() - { - Exiled.Events.Handlers.Server.RoundStarted -= roundHandler!.OnRoundStarted; - - Exiled.Events.Handlers.Player.ChangingItem -= playerHandler!.OnChangingItem; - - harmony?.UnpatchAll(); - - base.OnDisabled(); - } - } -} \ No newline at end of file diff --git a/Exiled.CustomItems/Exiled.CustomItems.csproj b/Exiled.CustomItems/Exiled.CustomItems.csproj deleted file mode 100644 index 8bbba33f4d..0000000000 --- a/Exiled.CustomItems/Exiled.CustomItems.csproj +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - Library - Exiled.CustomItems - true - Debug;Release;Installer - AnyCPU - enable - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if not "$(EXILED_DEV_REFERENCES)"=="" copy /y "$(OutputPath)$(AssemblyName).dll" "$(EXILED_DEV_REFERENCES)\Plugins\" - - - if [[ ! -z "$EXILED_DEV_REFERENCES" ]]; then cp "$(OutputPath)$(AssemblyName).dll" "$EXILED_DEV_REFERENCES/Plugins/"; fi - - diff --git a/Exiled.CustomModules/API/Enums/ModuleType.cs b/Exiled.CustomModules/API/Enums/ModuleType.cs new file mode 100644 index 0000000000..2118cffa80 --- /dev/null +++ b/Exiled.CustomModules/API/Enums/ModuleType.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Enums +{ + /// + /// Enumerates the available module types. + /// + public enum ModuleType + { + /// + /// Custom items module. + /// + CustomItems, + + /// + /// Custom abilities module. + /// + CustomAbilities, + + /// + /// Custom escapes module. + /// + CustomEscapes, + + /// + /// Custom roles module. + /// + CustomRoles, + + /// + /// Custom teams module. + /// + CustomTeams, + + /// + /// Custom game modes module. + /// + CustomGameModes, + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Enums/UEMatchState.cs b/Exiled.CustomModules/API/Enums/UEMatchState.cs index 986172eae1..a5633eae17 100644 --- a/Exiled.CustomModules/API/Enums/UEMatchState.cs +++ b/Exiled.CustomModules/API/Enums/UEMatchState.cs @@ -7,7 +7,7 @@ namespace Exiled.CustomModules.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// All available match states. diff --git a/Exiled.CustomModules/API/Enums/UUCustomAbilityType.cs b/Exiled.CustomModules/API/Enums/UUCustomAbilityType.cs new file mode 100644 index 0000000000..2e20a993b2 --- /dev/null +++ b/Exiled.CustomModules/API/Enums/UUCustomAbilityType.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Enums +{ + using Exiled.API.Features.Core.Generic; + + /// + /// Represents the base enum class for all available custom abilities. + /// + public class UUCustomAbilityType : UniqueUnmanagedEnumClass + { + /// + /// Represents an invalid custom ability. + /// + public static readonly UUCustomAbilityType None = new(); + } +} diff --git a/Exiled.CustomModules/API/Enums/UUCustomEscapeType.cs b/Exiled.CustomModules/API/Enums/UUCustomEscapeType.cs index 647b03bed2..2f43d48317 100644 --- a/Exiled.CustomModules/API/Enums/UUCustomEscapeType.cs +++ b/Exiled.CustomModules/API/Enums/UUCustomEscapeType.cs @@ -7,7 +7,7 @@ namespace Exiled.CustomModules.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// Represents the base enum class for all available custom escapes. diff --git a/Exiled.CustomModules/API/Enums/UUCustomItem.cs b/Exiled.CustomModules/API/Enums/UUCustomItemType.cs similarity index 67% rename from Exiled.CustomModules/API/Enums/UUCustomItem.cs rename to Exiled.CustomModules/API/Enums/UUCustomItemType.cs index d620953d0f..49af1efb86 100644 --- a/Exiled.CustomModules/API/Enums/UUCustomItem.cs +++ b/Exiled.CustomModules/API/Enums/UUCustomItemType.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // @@ -7,16 +7,16 @@ namespace Exiled.CustomModules.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// Represents the base enum class for all available custom items. /// - public class UUCustomItem : UniqueUnmanagedEnumClass + public class UUCustomItemType : UniqueUnmanagedEnumClass { /// /// Represents an invalid custom item. /// - public static readonly UUCustomItem None = new(); + public static readonly UUCustomItemType None = new(); } } diff --git a/Exiled.CustomModules/API/Enums/UUCustomRoleType.cs b/Exiled.CustomModules/API/Enums/UUCustomRoleType.cs index 2f43b63e64..f013b890b4 100644 --- a/Exiled.CustomModules/API/Enums/UUCustomRoleType.cs +++ b/Exiled.CustomModules/API/Enums/UUCustomRoleType.cs @@ -7,7 +7,7 @@ namespace Exiled.CustomModules.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// Represents the base enum class for all available custom roles. diff --git a/Exiled.CustomModules/API/Enums/UUCustomTeamType.cs b/Exiled.CustomModules/API/Enums/UUCustomTeamType.cs index fa4d53d55e..ee4f77594f 100644 --- a/Exiled.CustomModules/API/Enums/UUCustomTeamType.cs +++ b/Exiled.CustomModules/API/Enums/UUCustomTeamType.cs @@ -7,7 +7,7 @@ namespace Exiled.CustomModules.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// Represents the base enum class for all available custom teams. diff --git a/Exiled.CustomModules/API/Enums/UUEscapeScenarioType.cs b/Exiled.CustomModules/API/Enums/UUEscapeScenarioType.cs index 599b52c7a3..4f7c928195 100644 --- a/Exiled.CustomModules/API/Enums/UUEscapeScenarioType.cs +++ b/Exiled.CustomModules/API/Enums/UUEscapeScenarioType.cs @@ -7,7 +7,7 @@ namespace Exiled.CustomModules.API.Enums { - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; /// /// Represents the base enum class for all available escape scenarios. diff --git a/Exiled.CustomModules/API/Enums/UUGameModeType.cs b/Exiled.CustomModules/API/Enums/UUGameModeType.cs new file mode 100644 index 0000000000..38836bc4c3 --- /dev/null +++ b/Exiled.CustomModules/API/Enums/UUGameModeType.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Enums +{ + using Exiled.API.Features.Core.Generic; + + /// + /// Represents the base enum class for all available custom teams. + /// + public class UUGameModeType : UniqueUnmanagedEnumClass + { + /// + /// Represents an invalid custom game mode. + /// + public static readonly UUGameModeType None = new(); + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/Attributes/CustomAbilityAttribute.cs b/Exiled.CustomModules/API/Features/Attributes/CustomAbilityAttribute.cs index aef71637e0..ebc15219d4 100644 --- a/Exiled.CustomModules/API/Features/Attributes/CustomAbilityAttribute.cs +++ b/Exiled.CustomModules/API/Features/Attributes/CustomAbilityAttribute.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.Attributes { using System; diff --git a/Exiled.CustomModules/API/Features/Attributes/CustomEscapeAttribute.cs b/Exiled.CustomModules/API/Features/Attributes/CustomEscapeAttribute.cs index a1926b68b4..50ff23a9b9 100644 --- a/Exiled.CustomModules/API/Features/Attributes/CustomEscapeAttribute.cs +++ b/Exiled.CustomModules/API/Features/Attributes/CustomEscapeAttribute.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.Attributes { using System; diff --git a/Exiled.CustomModules/API/Features/Attributes/CustomGameModeAttribute.cs b/Exiled.CustomModules/API/Features/Attributes/CustomGameModeAttribute.cs index f81556e02a..4486068be1 100644 --- a/Exiled.CustomModules/API/Features/Attributes/CustomGameModeAttribute.cs +++ b/Exiled.CustomModules/API/Features/Attributes/CustomGameModeAttribute.cs @@ -5,10 +5,12 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.Attributes { using System; + using Exiled.CustomModules.API.Features.CustomGameModes; + /// /// This attribute determines whether the class which is being applied to should be treated as . /// diff --git a/Exiled.CustomModules/API/Features/Attributes/CustomItemAttribute.cs b/Exiled.CustomModules/API/Features/Attributes/CustomItemAttribute.cs index eae3d80cd0..d8312f4f86 100644 --- a/Exiled.CustomModules/API/Features/Attributes/CustomItemAttribute.cs +++ b/Exiled.CustomModules/API/Features/Attributes/CustomItemAttribute.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.Attributes { using System; diff --git a/Exiled.CustomModules/API/Features/Attributes/CustomRoleAttribute.cs b/Exiled.CustomModules/API/Features/Attributes/CustomRoleAttribute.cs index d6dfa91836..df9359813e 100644 --- a/Exiled.CustomModules/API/Features/Attributes/CustomRoleAttribute.cs +++ b/Exiled.CustomModules/API/Features/Attributes/CustomRoleAttribute.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.Attributes { using System; diff --git a/Exiled.CustomModules/API/Features/Attributes/CustomTeamAttribute.cs b/Exiled.CustomModules/API/Features/Attributes/CustomTeamAttribute.cs index 68a2718cff..fec2ab236f 100644 --- a/Exiled.CustomModules/API/Features/Attributes/CustomTeamAttribute.cs +++ b/Exiled.CustomModules/API/Features/Attributes/CustomTeamAttribute.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.Attributes { using System; diff --git a/Exiled.CustomModules/API/Features/CustomAbilities/AbilityInputComponent.cs b/Exiled.CustomModules/API/Features/CustomAbilities/AbilityInputComponent.cs index fa4db2eed4..b7905ee86c 100644 --- a/Exiled.CustomModules/API/Features/CustomAbilities/AbilityInputComponent.cs +++ b/Exiled.CustomModules/API/Features/CustomAbilities/AbilityInputComponent.cs @@ -12,8 +12,8 @@ namespace Exiled.CustomModules.API.Features.CustomAbilities using System.Text; using Exiled.API.Features; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Input; - using Exiled.API.Features.Pools; using Exiled.API.Features.Roles; using Exiled.CustomModules.API.Features.CustomAbilities.Settings; using Exiled.CustomModules.API.Features.PlayerAbilities; @@ -54,7 +54,7 @@ protected override void UnsubscribeEvents() /// /// defines an action which displays information about the ability. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected override bool InputCondition_KT0() => PressCount == 1 && Owner.Role is FpcRole { MoveState: PlayerMovementState.Sneaking }; /// @@ -62,7 +62,7 @@ protected override void UnsubscribeEvents() /// /// defines an action which activates the ability. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected override bool InputCondition_KT1() => PressCount == 1; /// @@ -70,7 +70,7 @@ protected override void UnsubscribeEvents() /// /// defines an action which switches backward. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected override bool InputCondition_KT2() => PressCount == 2 && Owner.Role is FpcRole { MoveState: PlayerMovementState.Sneaking }; /// @@ -78,7 +78,7 @@ protected override void UnsubscribeEvents() /// /// defines an action which switches forward. /// - /// if the condition was satified; otherwise, . + /// if the condition was satisfied; otherwise, . protected override bool InputCondition_KT3() => PressCount == 2; /// diff --git a/Exiled.CustomModules/API/Features/CustomAbilities/AbilityTracker.cs b/Exiled.CustomModules/API/Features/CustomAbilities/AbilityTracker.cs index 011d97f8fe..b14508264b 100644 --- a/Exiled.CustomModules/API/Features/CustomAbilities/AbilityTracker.cs +++ b/Exiled.CustomModules/API/Features/CustomAbilities/AbilityTracker.cs @@ -5,10 +5,8 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.CustomAbilities { - using Exiled.CustomModules.API.Features.CustomAbilities; - /// /// The actor which handles all tracking-related tasks for item abilities. /// diff --git a/Exiled.CustomModules/API/Features/CustomAbilities/CustomAbility.cs b/Exiled.CustomModules/API/Features/CustomAbilities/CustomAbility.cs index 3e1d42dd82..cfe1e55cc4 100644 --- a/Exiled.CustomModules/API/Features/CustomAbilities/CustomAbility.cs +++ b/Exiled.CustomModules/API/Features/CustomAbilities/CustomAbility.cs @@ -8,7 +8,6 @@ namespace Exiled.CustomModules.API.Features.CustomAbilities { using System; - using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -19,11 +18,12 @@ namespace Exiled.CustomModules.API.Features.CustomAbilities using Exiled.API.Features.Core; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.Attributes; using Exiled.CustomModules.API.Features.CustomAbilities.Settings; using Exiled.CustomModules.API.Features.CustomEscapes; using Exiled.CustomModules.Events.EventArgs.CustomAbilities; using HarmonyLib; - using Utils.NonAllocLINQ; /// @@ -143,6 +143,13 @@ public abstract class CustomAbility : CustomModule, ICustomAbility /// protected Type ReflectedGenericType => reflectedGenericType ??= GetType().GetGenericArguments()[0]; + /// + /// Gets a based on the provided id or . + /// + /// The id or of the custom ability. + /// The with the specified id, or if no ability is found. + public static CustomAbility Get(object id) => id is uint or UUCustomAbilityType ? Get((uint)id) : null; + /// /// Gets a given the specified id. /// @@ -465,10 +472,20 @@ public static void RemoveRange(T entity, IEnumerable ids) /// Enables all the custom abilities present in the assembly. /// /// A of which contains all the enabled custom abilities. - public static IEnumerable> EnableAll() + public static IEnumerable> EnableAll() => EnableAll(Assembly.GetCallingAssembly()); + + /// + /// Enables all the custom abilities present in the assembly. + /// + /// The assembly to enable the abilities from. + /// A of which contains all the enabled custom abilities. + public static IEnumerable> EnableAll(Assembly assembly) { + if (!CustomModules.Instance.Config.Modules.Contains(ModuleType.CustomAbilities)) + throw new Exception("ModuleType::CustomAbilities must be enabled in order to load any custom abilities"); + List> customAbilities = new(); - foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + foreach (Type type in assembly.GetTypes()) { CustomAbilityAttribute attribute = type.GetCustomAttribute(); if (!typeof(CustomAbility).IsAssignableFrom(type) || attribute is null) diff --git a/Exiled.CustomModules/API/Features/CustomAbilities/Types/AbilityBehaviourBase.cs b/Exiled.CustomModules/API/Features/CustomAbilities/Types/AbilityBehaviourBase.cs index 2c8f5d0f97..04620bc349 100644 --- a/Exiled.CustomModules/API/Features/CustomAbilities/Types/AbilityBehaviourBase.cs +++ b/Exiled.CustomModules/API/Features/CustomAbilities/Types/AbilityBehaviourBase.cs @@ -10,7 +10,7 @@ namespace Exiled.CustomModules.API.Features.CustomAbilities using System.Reflection; using Exiled.API.Features.Core; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; using Exiled.CustomModules.API.Features.CustomAbilities.Settings; diff --git a/Exiled.CustomModules/API/Features/CustomEscapes/CustomEscape.cs b/Exiled.CustomModules/API/Features/CustomEscapes/CustomEscape.cs index de8b594a9b..21287bb716 100644 --- a/Exiled.CustomModules/API/Features/CustomEscapes/CustomEscape.cs +++ b/Exiled.CustomModules/API/Features/CustomEscapes/CustomEscape.cs @@ -8,7 +8,6 @@ namespace Exiled.CustomModules.API.Features.CustomEscapes { using System; - using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -18,8 +17,8 @@ namespace Exiled.CustomModules.API.Features.CustomEscapes using Exiled.API.Features.Core; using Exiled.API.Features.Core.Interfaces; using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.Attributes; using MonoMod.Utils; - using Utils.NonAllocLINQ; /// @@ -88,46 +87,11 @@ public abstract class CustomEscape : CustomModule, IAdditiveBehaviour public virtual List Settings { get; } = new() { EscapeSettings.Default, }; /// - /// Enables all the custom escapes present in the assembly. + /// Gets a based on the provided id or . /// - /// A of containing all enabled custom escapes. - public static List EnableAll() - { - List customEscapes = new(); - foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) - { - CustomEscapeAttribute attribute = type.GetCustomAttribute(); - if (!typeof(CustomEscape).IsAssignableFrom(type) || attribute is null) - continue; - - CustomEscape customEscape = Activator.CreateInstance(type) as CustomEscape; - - if (!customEscape.IsEnabled) - continue; - - if (customEscape.TryRegister(attribute)) - customEscapes.Add(customEscape); - } - - if (customEscapes.Count() != List.Count()) - Log.Info($"{customEscapes.Count()} custom escapes have been successfully registered!"); - - return customEscapes; - } - - /// - /// Disables all the custom escapes present in the assembly. - /// - /// A of containing all disabled custom escapes. - public static List DisableAll() - { - List customEscapes = new(); - customEscapes.AddRange(List.Where(customEscape => customEscape.TryUnregister())); - - Log.Info($"{customEscapes.Count()} custom escapes have been successfully unregistered!"); - - return customEscapes; - } + /// The id or of the custom escape. + /// The with the specified id, or if no escape is found. + public static CustomEscape Get(object id) => id is uint or UUCustomEscapeType ? Get((uint)id) : null; /// /// Gets a given the specified . @@ -166,6 +130,14 @@ public static CustomEscape Get(Type type) => /// The matching the search or if not registered. public static CustomEscape Get(Player player) => PlayersValue.TryGetValue(player, out CustomEscape customEscape) ? customEscape : default; + /// + /// Attempts to retrieve a based on the provided id or . + /// + /// The id or of the custom escape. + /// When this method returns, contains the associated with the specified id, if the id was found; otherwise, . + /// if a was found; otherwise, . + public static bool TryGet(object id, out CustomEscape customEscape) => customEscape = Get(id); + /// /// Tries to get a given the specified . /// @@ -198,6 +170,58 @@ public static CustomEscape Get(Type type) => /// if a was found; otherwise, . public static bool TryGet(Type type, out CustomEscape customEscape) => customEscape = Get(type.GetType()); + /// + /// Enables all the custom escapes present in the assembly. + /// + /// A of containing all enabled custom escapes. + public static List EnableAll() => EnableAll(Assembly.GetCallingAssembly()); + + /// + /// Enables all the custom escapes present in the assembly. + /// + /// The assembly to enable the escapes from. + /// A of containing all enabled custom escapes. + public static List EnableAll(Assembly assembly) + { + if (!CustomModules.Instance.Config.Modules.Contains(ModuleType.CustomEscapes)) + throw new Exception("ModuleType::CustomEscapes must be enabled in order to load any custom escapes"); + + List customEscapes = new(); + foreach (Type type in assembly.GetTypes()) + { + CustomEscapeAttribute attribute = type.GetCustomAttribute(); + if (!typeof(CustomEscape).IsAssignableFrom(type) || attribute is null) + continue; + + CustomEscape customEscape = Activator.CreateInstance(type) as CustomEscape; + + if (!customEscape.IsEnabled) + continue; + + if (customEscape.TryRegister(attribute)) + customEscapes.Add(customEscape); + } + + if (customEscapes.Count() != List.Count()) + Log.Info($"{customEscapes.Count()} custom escapes have been successfully registered!"); + + return customEscapes; + } + + /// + /// Disables all the custom escapes present in the assembly. + /// + /// A of containing all disabled custom escapes. + public static List DisableAll() + { + List customEscapes = new(); + customEscapes.AddRange(List.Where(customEscape => customEscape.TryUnregister())); + + Log.Info($"{customEscapes.Count()} custom escapes have been successfully unregistered!"); + + return customEscapes; + } + /// /// Attaches a with the specified to the specified . /// diff --git a/Exiled.CustomModules/API/Features/CustomEscapes/EscapeBehaviour.cs b/Exiled.CustomModules/API/Features/CustomEscapes/EscapeBehaviour.cs index 9ac86ffca3..fb1a93af35 100644 --- a/Exiled.CustomModules/API/Features/CustomEscapes/EscapeBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomEscapes/EscapeBehaviour.cs @@ -13,6 +13,7 @@ namespace Exiled.CustomModules.API.Features.CustomEscapes using Exiled.API.Features; using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; using Exiled.CustomModules.API.Enums; diff --git a/Exiled.CustomModules/API/Features/CustomEscapes/Generics/CustomEscape.cs b/Exiled.CustomModules/API/Features/CustomEscapes/Generic/CustomEscape.cs similarity index 100% rename from Exiled.CustomModules/API/Features/CustomEscapes/Generics/CustomEscape.cs rename to Exiled.CustomModules/API/Features/CustomEscapes/Generic/CustomEscape.cs diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/CustomGameMode.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/CustomGameMode.cs index 1580b459c0..cfcbffa54e 100644 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/CustomGameMode.cs +++ b/Exiled.CustomModules/API/Features/CustomGamemodes/CustomGameMode.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.CustomGameModes { using System; using System.Collections.Generic; @@ -16,6 +16,9 @@ namespace Exiled.CustomModules.API.Features using Exiled.API.Features; using Exiled.API.Features.Core; using Exiled.API.Features.Core.Interfaces; + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.Attributes; + using Unity.Collections.LowLevel.Unsafe; /// /// Represents a custom game mode in the system, derived from and implementing . @@ -87,6 +90,38 @@ public abstract class CustomGameMode : CustomModule, IAdditiveBehaviours /// public virtual GameModeSettings Settings { get; } + /// + /// Gets a value indicating whether the game mode can start automatically based on the configured probability, if automatic. + /// + /// if the game mode can start automatically; otherwise, . + public bool CanStartAuto => Settings.Automatic && Settings.AutomaticProbability.EvaluateProbability(); + + /// + /// Gets the type of the game state. + /// + /// The type of the game state if found; otherwise, . + public Type GameState => BehaviourComponents.FirstOrDefault(comp => typeof(GameState).IsAssignableFrom(comp)); + + /// + /// Gets the types of the player states. + /// + /// The types of the player states if found; otherwise, empty. + public IEnumerable PlayerStates => BehaviourComponents.Where(comp => typeof(PlayerState).IsAssignableFrom(comp)); + + /// + /// Gets all instances based on the predicate. + /// + /// The predicate. + /// All instances matching the predicate. + public static IEnumerable Get(Func predicate) => List.Where(predicate); + + /// + /// Gets a based on the provided id or . + /// + /// The id or of the custom game mode. + /// The with the specified id, or if no game mode is found. + public static CustomGameMode Get(object id) => id is uint or UUGameModeType ? Get((uint)id) : null; + /// /// Gets a given the specified . /// @@ -124,6 +159,14 @@ public static CustomGameMode Get(Type type) => /// The matching the search or if not found. public static CustomGameMode Get(PlayerState playerState) => Get(playerState.GetType()); + /// + /// Attempts to retrieve a based on the provided id or . + /// + /// The id or of the custom game mode. + /// When this method returns, contains the associated with the specified id, if the id was found; otherwise, . + /// if a was found; otherwise, . + public static bool TryGet(object id, out CustomGameMode customGameMode) => customGameMode = Get(id); + /// /// Tries to get a given the specified . /// @@ -152,10 +195,20 @@ public static CustomGameMode Get(Type type) => /// Enables all the custom game modes present in the assembly. /// /// A of containing all enabled custom game modes. - public static List EnableAll() + public static List EnableAll() => EnableAll(Assembly.GetCallingAssembly()); + + /// + /// Enables all the custom game modes present in the assembly. + /// + /// The assembly to enable the game modes from. + /// A of containing all enabled custom game modes. + public static List EnableAll(Assembly assembly) { + if (!CustomModules.Instance.Config.Modules.Contains(ModuleType.CustomGameModes)) + throw new Exception("ModuleType::CustomGameModes must be enabled in order to load any custom game modes"); + List customGameModes = new(); - foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) + foreach (Type type in assembly.GetTypes()) { CustomGameModeAttribute attribute = type.GetCustomAttribute(); if (!typeof(CustomGameMode).IsAssignableFrom(type) || attribute is null) @@ -166,6 +219,13 @@ public static List EnableAll() if (!customGameMode.IsEnabled) continue; + if (customGameMode.BehaviourComponents.Count(comp => typeof(GameState).IsAssignableFrom(comp)) != 1 || customGameMode.PlayerStates.Count() <= 0) + { + Log.Error($"Failed to load the custom game mode.\n" + + $"The game mode \"{customGameMode.Name}\" should have exactly one GameState component and at least one PlayerState component defined."); + continue; + } + if (customGameMode.TryRegister(attribute)) customGameModes.Add(customGameMode); } @@ -183,7 +243,7 @@ public static List EnableAll() public static List DisableAll() { List customGameModes = new(); - customGameModes.AddRange(List.Where(customGamemode => customGamemode.TryUnregister())); + customGameModes.AddRange(List.Where(customGameMode => customGameMode.TryUnregister())); Log.Info($"{customGameModes.Count()} custom game modes have been successfully unregistered!"); diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs index 209994361f..d994390f08 100644 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs +++ b/Exiled.CustomModules/API/Features/CustomGamemodes/GameModeSettings.cs @@ -5,15 +5,204 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.CustomGameModes { + using System; + + using Exiled.API.Enums; using Exiled.API.Features.Core; using Exiled.API.Features.Core.Interfaces; + using Exiled.CustomModules.API.Enums; + using PlayerRoles; + using Respawning; /// /// A tool to easily setup game modes. /// public class GameModeSettings : TypeCastObject, IAdditiveProperty { + /// + /// Gets or sets a value indicating whether the game mode operates in automatic mode. + /// + /// + /// In automatic mode, the behavior of the game mode is managed automatically by the . + ///
+ /// The game mode will start automatically if the specified probability condition is met and the minimum player requirement () is satisfied. + ///
+ public virtual bool Automatic { get; set; } + + /// + /// Gets or sets the probability condition for automatic game mode activation. + /// + /// + /// This property specifies the probability condition that determines whether the game mode will start automatically. + ///
+ /// If the specified probability condition is met and the minimum player requirement () is satisfied, the game mode will activate automatically. + ///
+ public virtual float AutomaticProbability { get; set; } + + /// + /// Gets or sets the minimum amount of players to start the game mode. + /// + public virtual uint MinimumPlayers { get; set; } + + /// + /// Gets or sets the maximum allowed amount of players managed by the game mode. + /// + public virtual uint MaximumPlayers { get; set; } + + /// + /// Gets or sets a value indicating whether the exceeding players should be rejected. + /// + public virtual bool RejectExceedingPlayers { get; set; } + + /// + /// Gets or sets the message to be displayed when a player is rejected due to exceeding amount of players. + /// + public virtual string RejectExceedingMessage { get; set; } + + /// + /// Gets or sets a value indicating whether the players can respawn. + /// + public virtual bool IsRespawnEnabled { get; set; } + + /// + /// Gets or sets the respawn time for individual players. + /// + public virtual float RespawnTime { get; set; } + + /// + /// Gets or sets a value indicating whether teams can regularly respawn. + /// + public virtual bool IsTeamRespawnEnabled { get; set; } + + /// + /// Gets or sets the respawn time for individual teams. + /// + public virtual int TeamRespawnTime { get; set; } + + /// + /// Gets or sets a value indicating whether custom ending conditions should be used over predefined conditions. + /// + public virtual bool UseCustomEndingConditions { get; set; } + + /// + /// Gets or sets a value indicating whether server should be restarted when the game mode ends. + /// + public virtual bool RestartRoundOnEnd { get; set; } + + /// + /// Gets or sets the amount of time to await before restarting the server. + /// + public virtual float RestartWindupTime { get; set; } + + /// + /// Gets or sets a [] containing all zones that should be permanently locked. + /// + public virtual ZoneType[] LockedZones { get; set; } + + /// + /// Gets or sets a [] containing all doors that should be permanently locked. + /// + public virtual DoorType[] LockedDoors { get; set; } + + /// + /// Gets or sets a [] containing all elevators that should be permanently locked. + /// + public virtual ElevatorType[] LockedElevators { get; set; } + + /// + /// Gets or sets a value indicating whether the decontamination should be enabled. + /// + public virtual bool IsDecontaminationEnabled { get; set; } + + /// + /// Gets or sets a value indicating whether the Alpha Warhead is enabled. + /// + public virtual bool IsWarheadEnabled { get; set; } + + /// + /// Gets or sets a value indicating whether the Alpha Warhead interactions are allowed. + /// + public virtual bool IsWarheadInteractable { get; set; } + + /// + /// Gets or sets the amount of time, expressed in seconds, after which the Alpha Warhead will be automatically started. + /// + /// must be set to . + /// + public virtual float AutoWarheadTime { get; set; } + + /// + /// Gets or sets a [] containing all spawnable roles. + /// + /// If not empty, all undefined roles won't be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual RoleTypeId[] SpawnableRoles { get; set; } + + /// + /// Gets or sets a [] containing all spawnable custom roles. + /// + /// If not empty, all undefined custom roles won't be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual uint[] SpawnableCustomRoles { get; set; } + + /// + /// Gets or sets a [] containing all spawnable teams. + /// + /// If not empty, all undefined teams won't be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual SpawnableTeamType[] SpawnableTeams { get; set; } + + /// + /// Gets or sets a [] containing all spawnable custom teams. + /// + /// If not empty, all undefined custom teams won't be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual uint[] SpawnableCustomTeams { get; set; } + + /// + /// Gets or sets a [] containing all non spawnable roles. + /// + /// If not empty, all undefined roles will be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual RoleTypeId[] NonSpawnableRoles { get; set; } + + /// + /// Gets or sets a [] containing all non spawnable custom roles. + /// + /// If not empty, all undefined custom roles will be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual uint[] NonSpawnableCustomRoles { get; set; } + + /// + /// Gets or sets a [] containing all non spawnable custom teams. + /// + /// If not empty, all undefined teams will be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual SpawnableTeamType[] NonSpawnableTeams { get; set; } + + /// + /// Gets or sets a [] containing all non spawnable custom teams. + /// + /// If not empty, all undefined custom teams will be able to spawn. + ///
+ /// It's highly recommended to not use it along with . + ///
+ public virtual uint[] NonSpawnableCustomTeams { get; set; } } } \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/GameState.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/GameState.cs index 8fb8ddcc2e..0b402d3f30 100644 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/GameState.cs +++ b/Exiled.CustomModules/API/Features/CustomGamemodes/GameState.cs @@ -5,18 +5,34 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.CustomGameModes { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Reflection; + using System.Runtime.InteropServices.WindowsRuntime; + using Exiled.API.Enums; + using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Core; using Exiled.API.Features.Core.Interfaces; + using Exiled.API.Features.Doors; + using Exiled.API.Features.Roles; using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.CustomRoles; + using Exiled.CustomModules.Events.EventArgs.CustomRoles; using Exiled.Events.EventArgs.Player; + using Exiled.Events.EventArgs.Server; + using GameCore; + using Interactables.Interobjects.DoorUtils; + using LightContainmentZoneDecontamination; + using PlayerRoles; + using Respawning; + using UnityStandardAssets.CinematicEffects; + using Utils.Networking; /// /// Represents the state of the game on the server within the custom game mode, derived from and implementing . @@ -35,7 +51,7 @@ public abstract class GameState : EActor, IAdditiveSettings private Type cachedPlayerStateType; /// - /// Gets the relative . + /// Gets the relative . /// public CustomGameMode CustomGameMode { get; private set; } @@ -74,6 +90,11 @@ public abstract class GameState : EActor, IAdditiveSettings /// public bool CanBeEnded => EvaluateEndingConditions(); + /// + /// Gets the current player count. + /// + public int PlayerCount => playerStates.Count; + /// public virtual void AdjustAdditivePipe() { @@ -110,12 +131,39 @@ public virtual void AdjustAdditivePipe() /// /// Starts the . /// - public virtual void Start() + /// A value indicating whether the should be started regardless any conditions. + public virtual void Start(bool isForced = false) { + if (!typeof(World).IsAssignableFrom(new StackTrace().GetFrame(1)?.GetMethod()?.DeclaringType)) + throw new Exception("Only the World can start a GameState."); + + if (!isForced && PlayerCount < Settings.MinimumPlayers) + return; + + World.Get().RunningGameMode = CustomGameMode.Get(this).Id; + foreach (PlayerState ps in PlayerStates) ps.Deploy(); } + /// + /// Ends the . + /// + /// A value indicating whether the should be ended regardless any conditions. + public virtual void End(bool isForced = false) + { + if (!typeof(GameState).IsAssignableFrom(new StackTrace().GetFrame(1)?.GetMethod()?.DeclaringType)) + throw new Exception("Only the World can end a GameState."); + + if (!isForced && !CanBeEnded) + return; + + World.Get().RunningGameMode = UUGameModeType.None; + + foreach (PlayerState ps in PlayerStates) + ps.Destroy(); + } + /// /// Defines and evaluates the ending conditions. /// @@ -127,6 +175,18 @@ protected override void PostInitialize() { base.PostInitialize(); + RespawnManager.Singleton._started = Settings.IsTeamRespawnEnabled; + + if (Settings.TeamRespawnTime > 0f) + RespawnManager.Singleton.TimeTillRespawn = Settings.TeamRespawnTime; + + if (!Settings.IsDecontaminationEnabled) + DecontaminationController.Singleton.NetworkDecontaminationOverride = DecontaminationController.DecontaminationStatus.Disabled; + + Warhead.IsLocked = Settings.IsWarheadEnabled; + Warhead.AutoDetonate = Settings.AutoWarheadTime > 0f; + Warhead.AutoDetonateTime = Settings.AutoWarheadTime; + foreach (Player player in Player.List) { if (player.HasComponent(PlayerStateComponent, true)) @@ -136,11 +196,42 @@ protected override void PostInitialize() } } + /// + protected override void OnBeginPlay() + { + base.OnBeginPlay(); + + Door.LockAll(Settings.LockedZones); + Door.LockAll(Settings.LockedDoors); + Lift.LockAll(Settings.LockedElevators); + } + + /// + protected override void OnEndPlay() + { + base.OnEndPlay(); + + Door.UnlockAll(); + Lift.UnlockAll(); + } + /// protected override void SubscribeEvents() { base.SubscribeEvents(); + StaticActor.Get().AssigningHumanCustomRolesDispatcher.Bind(this, OnAssigningCustomHumanRolesInternal); + StaticActor.Get().AssigningScpCustomRolesDispatcher.Bind(this, OnAssigningCustomScpRolesInternal); + + StaticActor.Get().SelectingCustomTeamRespawnDispatcher.Bind(this, OnSelectingCustomTeamRespawnInternal); + + Exiled.Events.Handlers.Server.AssigningHumanRoles += OnAssigningHumanRolesInternal; + Exiled.Events.Handlers.Server.AssigningScpRoles += OnAssigningScpRolesInternal; + Exiled.Events.Handlers.Server.EndingRound += OnEndingRoundInternal; + Exiled.Events.Handlers.Server.RespawningTeam += OnRespawningTeamInternal; + Exiled.Events.Handlers.Server.RespawnedTeam += OnRespawnedTeamInternal; + + Exiled.Events.Handlers.Player.PreAuthenticating += OnPreAuthenticatingInternal; Exiled.Events.Handlers.Player.Verified += OnVerifiedInternal; Exiled.Events.Handlers.Player.Destroying += OnDestroyingInternal; } @@ -150,16 +241,294 @@ protected override void UnsubscribeEvents() { base.UnsubscribeEvents(); + StaticActor.Get().AssigningHumanCustomRolesDispatcher.Unbind(this); + StaticActor.Get().SelectingCustomTeamRespawnDispatcher.Unbind(this); + + Exiled.Events.Handlers.Server.AssigningHumanRoles -= OnAssigningHumanRolesInternal; + Exiled.Events.Handlers.Server.AssigningScpRoles -= OnAssigningScpRolesInternal; + Exiled.Events.Handlers.Server.EndingRound -= OnEndingRoundInternal; + Exiled.Events.Handlers.Server.RespawningTeam -= OnRespawningTeamInternal; + Exiled.Events.Handlers.Server.RespawnedTeam -= OnRespawnedTeamInternal; + + Exiled.Events.Handlers.Player.PreAuthenticating -= OnPreAuthenticatingInternal; Exiled.Events.Handlers.Player.Verified -= OnVerifiedInternal; Exiled.Events.Handlers.Player.Destroying -= OnDestroyingInternal; } + private void OnAssigningHumanRolesInternal(AssigningHumanRolesEventArgs ev) + { + if (!Settings.SpawnableRoles.IsEmpty()) + { + IEnumerable roles = ev.Roles.Where(role => Settings.SpawnableRoles.Contains(role)); + int amount = roles.Count(); + if (amount < ev.Roles.Length) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(Role.Random(false, Settings.SpawnableRoles.Except(ev.Roles))); + + ev.Roles = roles.Concat(newRoles).ToArray(); + } + + return; + } + + if (!Settings.NonSpawnableRoles.IsEmpty()) + { + IEnumerable roles = ev.Roles.Where(role => !Settings.NonSpawnableRoles.Contains(role)); + int amount = roles.Count(); + if (amount < ev.Roles.Length) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(Role.Random(false, Settings.NonSpawnableRoles)); + + ev.Roles = roles.Concat(newRoles).ToArray(); + } + } + } + + private void OnAssigningScpRolesInternal(AssigningScpRolesEventArgs ev) + { + if (!Settings.SpawnableRoles.IsEmpty()) + { + IEnumerable roles = ev.Roles.Where(role => Settings.SpawnableRoles.Contains(role)); + int amount = roles.Count(); + if (amount < ev.Roles.Count) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(Role.Random(false, Settings.SpawnableRoles.Except(ev.Roles))); + + ev.Roles = roles.Concat(newRoles).ToList(); + } + + return; + } + + if (!Settings.NonSpawnableRoles.IsEmpty()) + { + IEnumerable roles = ev.Roles.Where(role => !Settings.NonSpawnableRoles.Contains(role)); + int amount = roles.Count(); + if (amount < ev.Roles.Count) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(Role.Random(false, Settings.NonSpawnableRoles)); + + ev.Roles = roles.Concat(newRoles).ToList(); + } + } + } + + private void OnAssigningCustomHumanRolesInternal(Events.EventArgs.CustomRoles.AssigningHumanCustomRolesEventArgs ev) + { + if (!Settings.SpawnableCustomRoles.IsEmpty()) + { + List customRoles = new(); + foreach (object role in ev.Roles) + { + if (!CustomRole.TryGet(role, out CustomRole cr) || !Settings.SpawnableCustomRoles.Contains(cr.Id)) + continue; + + customRoles.Add(cr.Id); + } + + ev.Roles.RemoveAll(o => CustomRole.TryGet(o, out CustomRole cr) && !Settings.SpawnableCustomRoles.Contains(cr.Id)); + + int amount = customRoles.Count(); + if (amount < (ev.Roles.Count - customRoles.Count)) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(CustomRole.Get(role => !role.IsScp && !role.IsTeamUnit).Random().Id); + + ev.Roles = ev.Roles.Where(role => role is not RoleTypeId).Cast().Concat(newRoles.Cast()).ToList(); + } + + return; + } + + if (!Settings.NonSpawnableCustomRoles.IsEmpty()) + { + List customRoles = new(); + foreach (object role in ev.Roles) + { + if (!CustomRole.TryGet(role, out CustomRole cr) || Settings.NonSpawnableCustomRoles.Contains(cr.Id)) + continue; + + customRoles.Add(cr.Id); + } + + ev.Roles.RemoveAll(o => CustomRole.TryGet(o, out CustomRole cr) && Settings.NonSpawnableCustomRoles.Contains(cr.Id)); + + int amount = customRoles.Count(); + if (amount < (ev.Roles.Count - customRoles.Count)) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(CustomRole.Get(role => !role.IsScp && !role.IsTeamUnit).Random().Id); + + ev.Roles = ev.Roles.Where(role => role is not RoleTypeId).Cast().Concat(newRoles.Cast()).ToList(); + } + } + } + + private void OnAssigningCustomScpRolesInternal(Events.EventArgs.CustomRoles.AssigningScpCustomRolesEventArgs ev) + { + if (!Settings.SpawnableCustomRoles.IsEmpty()) + { + List customRoles = new(); + foreach (object role in ev.Roles) + { + if (!CustomRole.TryGet(role, out CustomRole cr) || !Settings.SpawnableCustomRoles.Contains(cr.Id)) + continue; + + customRoles.Add(cr.Id); + } + + ev.Roles.RemoveAll(o => CustomRole.TryGet(o, out CustomRole cr) && !Settings.SpawnableCustomRoles.Contains(cr.Id)); + + int amount = customRoles.Count(); + if (amount < (ev.Roles.Count - customRoles.Count)) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(CustomRole.Get(role => !role.IsScp && !role.IsTeamUnit).Random().Id); + + ev.Roles = ev.Roles.Where(role => role is not RoleTypeId).Cast().Concat(newRoles.Cast()).ToList(); + } + + return; + } + + if (!Settings.NonSpawnableCustomRoles.IsEmpty()) + { + List customRoles = new(); + foreach (object role in ev.Roles) + { + if (!CustomRole.TryGet(role, out CustomRole cr) || Settings.NonSpawnableCustomRoles.Contains(cr.Id)) + continue; + + customRoles.Add(cr.Id); + } + + ev.Roles.RemoveAll(o => CustomRole.TryGet(o, out CustomRole cr) && Settings.NonSpawnableCustomRoles.Contains(cr.Id)); + + int amount = customRoles.Count(); + if (amount < (ev.Roles.Count - customRoles.Count)) + { + List newRoles = new(); + + for (int i = 0; i < amount; i++) + newRoles.Add(CustomRole.Get(role => role.IsScp && !role.IsTeamUnit).Random().Id); + + ev.Roles = ev.Roles.Where(role => role is not RoleTypeId).Cast().Concat(newRoles.Cast()).ToList(); + } + } + } + + private void OnEndingRoundInternal(EndingRoundEventArgs ev) + { + if (!Settings.UseCustomEndingConditions || !EvaluateEndingConditions()) + return; + + ev.IsAllowed = true; + } + + private void OnSelectingRespawnTeamInternal(SelectingRespawnTeamEventArgs ev) + { + if (!Settings.SpawnableTeams.IsEmpty() && Settings.SpawnableTeams.Contains(ev.Team)) + { + if (ev.Team is SpawnableTeamType.ChaosInsurgency && !Settings.SpawnableTeams.Contains(SpawnableTeamType.NineTailedFox)) + { + ev.Team = SpawnableTeamType.NineTailedFox; + return; + } + + if (ev.Team is SpawnableTeamType.NineTailedFox && !Settings.SpawnableTeams.Contains(SpawnableTeamType.ChaosInsurgency)) + { + ev.Team = SpawnableTeamType.ChaosInsurgency; + return; + } + + ev.Team = SpawnableTeamType.None; + return; + } + + if (!Settings.NonSpawnableTeams.IsEmpty() && Settings.NonSpawnableTeams.Contains(ev.Team)) + { + if (ev.Team is SpawnableTeamType.ChaosInsurgency && !Settings.NonSpawnableTeams.Contains(SpawnableTeamType.NineTailedFox)) + { + ev.Team = SpawnableTeamType.NineTailedFox; + return; + } + + if (ev.Team is SpawnableTeamType.NineTailedFox && !Settings.NonSpawnableTeams.Contains(SpawnableTeamType.ChaosInsurgency)) + { + ev.Team = SpawnableTeamType.ChaosInsurgency; + return; + } + + ev.Team = SpawnableTeamType.None; + } + } + + private void OnSelectingCustomTeamRespawnInternal(SelectingCustomTeamRespawnEventArgs ev) + { + if (!CustomTeam.TryGet(ev.Team, out CustomTeam team)) + return; + + if (!Settings.SpawnableCustomTeams.IsEmpty() && Settings.SpawnableCustomTeams.Contains(team.Id)) + { + CustomTeam newTeam = CustomTeam.Get(t => Settings.SpawnableCustomTeams.Contains(t.Id)).Shuffle().FirstOrDefault(); + ev.Team = newTeam ? newTeam.Id : SpawnableTeamType.None; + return; + } + + if (!Settings.NonSpawnableCustomTeams.IsEmpty() && Settings.NonSpawnableCustomTeams.Contains(team.Id)) + { + CustomTeam newTeam = CustomTeam.Get(t => !Settings.NonSpawnableCustomTeams.Contains(t.Id)).Shuffle().FirstOrDefault(); + ev.Team = newTeam ? newTeam.Id : SpawnableTeamType.None; + } + } + + private void OnRespawningTeamInternal(RespawningTeamEventArgs ev) + { + if ((!Settings.SpawnableTeams.IsEmpty() && !Settings.SpawnableTeams.Contains(ev.NextKnownTeam)) || + (!Settings.NonSpawnableTeams.IsEmpty() && Settings.NonSpawnableTeams.Contains(ev.NextKnownTeam))) + ev.IsAllowed = false; + } + + private void OnRespawnedTeamInternal(RespawnedTeamEventArgs ev) + { + RespawnManager.Singleton.TimeTillRespawn = Settings.TeamRespawnTime; + } + + private void OnPreAuthenticatingInternal(PreAuthenticatingEventArgs ev) + { + if (PlayerCount == Settings.MaximumPlayers && Settings.RejectExceedingPlayers) + ev.Reject(Settings.RejectExceedingMessage, true); + } + private void OnVerifiedInternal(VerifiedEventArgs ev) { if (ev.Player.HasComponent(PlayerStateComponent, true)) return; - ev.Player.AddComponent(PlayerStateComponent); + EActor component = EObject.CreateDefaultSubobject(PlayerStateComponent, ev.Player.GameObject).Cast(); + + if(PlayerCount == Settings.MaximumPlayers) + component.As().IsRespawnable = false; + + ev.Player.AddComponent(component); } private void OnDestroyingInternal(DestroyingEventArgs ev) diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/PlayerState.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/PlayerState.cs index 6b87a23d92..e502985dae 100644 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/PlayerState.cs +++ b/Exiled.CustomModules/API/Features/CustomGamemodes/PlayerState.cs @@ -5,14 +5,21 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.API.Features +namespace Exiled.CustomModules.API.Features.CustomGameModes { using System; using System.Diagnostics; using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; using Exiled.API.Features.DynamicEvents; + using Exiled.Events.EventArgs.Player; + using Exiled.Events.EventArgs.Warhead; + using MEC; + using PlayerRoles; + using UnityStandardAssets.CinematicEffects; + using Utf8Json.Resolvers.Internal; /// /// Represents the state of an individual player within the custom game mode, derived from . @@ -27,21 +34,112 @@ namespace Exiled.CustomModules.API.Features /// public abstract class PlayerState : EPlayerBehaviour { + private bool isActive; + private CoroutineHandle onReadyHandle; + /// /// Gets the which handles all delegates to be fired after the has been deployed. /// [DynamicEventDispatcher] public static TDynamicEventDispatcher DeployedDispatcher { get; private set; } + /// + /// Gets the which handles all delegates to be fired after the has been activated. + /// + [DynamicEventDispatcher] + public static TDynamicEventDispatcher ActivatedDispatcher { get; private set; } + + /// + /// Gets the which handles all delegates to be fired after the has been deactivated. + /// + [DynamicEventDispatcher] + public static TDynamicEventDispatcher DeactivatedDispatcher { get; private set; } + + /// + /// Gets or sets a value indicating whether the can behave regularly. + /// + public virtual bool IsActive + { + get => isActive; + set + { + if (value == isActive) + return; + + if (value) + { + isActive = value; + ActivatedDispatcher.InvokeAll(this); + return; + } + + isActive = false; + DeactivatedDispatcher.InvokeAll(this); + } + } + + /// + /// Gets or sets a value indicating whether the is respawnable. + /// + public virtual bool IsRespawnable { get; set; } + + /// + /// Gets the respawn time for individual players. + /// + public float RespawnTime => World.Get().GameState.Settings.RespawnTime; + /// /// Gets or sets the score. /// public int Score { get; protected set; } /// - /// Gets or sets a value indicating whether the is respawnable. + /// Gets or sets the last time the player died. + /// + public DateTime LastDeath { get; protected set; } + + /// + /// Gets a value indicating whether the player is ready to respawn. /// - public bool IsRespawnable { get; set; } + public virtual bool CanRespawn => DateTime.Now > LastDeath + TimeSpan.FromSeconds(RespawnTime); + + /// + /// Gets or sets the remaining respawn time. + /// + public virtual float RemainingRespawnTime + { + get => (float)Math.Max(0f, (LastDeath + TimeSpan.FromSeconds(RespawnTime) - DateTime.Now).TotalSeconds); + set => LastDeath = LastDeath.AddSeconds(value); + } + + /// + /// Forces the respawn procedure. + /// + /// The new value, representing the updated death timestamp. + public virtual DateTime ForceRespawn() + { + Respawn(); + LastDeath = DateTime.Now - TimeSpan.FromSeconds(RespawnTime + 1); + return LastDeath; + } + + /// + /// Resets the respawn procedure. + /// + /// The new value, representing the updated death timestamp. + public virtual DateTime ResetRespawn() + { + if (onReadyHandle.IsRunning) + Timing.KillCoroutines(onReadyHandle); + + onReadyHandle = Timing.CallDelayed((DateTime.Now + TimeSpan.FromSeconds(RespawnTime)).Second + 1, () => + { + if (CanRespawn) + Respawn(); + }); + + return LastDeath = DateTime.Now; + } /// /// Deploys the in game. @@ -82,6 +180,24 @@ protected override void OnEndPlay() World.Get().GameState.RemovePlayerState(this); } + /// + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + + Exiled.Events.Handlers.Player.Died += OnDiedInternal; + Exiled.Events.Handlers.Player.InteractingElevator += OnInteractingElevatorInternal; + } + + /// + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + + Exiled.Events.Handlers.Player.Died -= OnDiedInternal; + Exiled.Events.Handlers.Player.InteractingElevator -= OnInteractingElevatorInternal; + } + /// /// Fired after a has been deployed. /// @@ -90,6 +206,31 @@ protected override void OnEndPlay() protected virtual void OnDeployed() { DeployedDispatcher.InvokeAll(this); + + if (IsActive) + OnDeployed_Active(); + else + OnDeployed_Inactive(); + } + + /// + /// Fired after a has been deployed and is active. + /// + /// It defines the initial state of the . + /// + protected virtual void OnDeployed_Active() + { + IsRespawnable = World.Get().GameState.Settings.IsRespawnEnabled; + } + + /// + /// Fired after a has been deployed and is not active. + /// + /// It defines the initial state of the . + /// + protected virtual void OnDeployed_Inactive() + { + Owner.Role.Set(RoleTypeId.Spectator); } /// @@ -100,5 +241,29 @@ protected virtual void Respawn() if (!IsRespawnable) return; } + + private void OnDiedInternal(DiedEventArgs ev) + { + if (!Check(ev.Player) || !IsRespawnable) + return; + + ResetRespawn(); + } + + private void OnInteractingElevatorInternal(InteractingElevatorEventArgs ev) + { + if (!Check(ev.Player)) + return; + + ev.IsAllowed = !ev.Lift.IsLocked && !World.Get().GameState.Settings.LockedElevators.Contains(ev.Lift.Type); + } + + private void OnInteractingWarhead(ChangingLeverStatusEventArgs ev) + { + GameModeSettings settings = World.Get().GameState.Settings; + + if (!settings.IsWarheadEnabled || !settings.IsWarheadInteractable) + ev.IsAllowed = false; + } } } \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomGamemodes/World.cs b/Exiled.CustomModules/API/Features/CustomGamemodes/World.cs deleted file mode 100644 index 73525260e6..0000000000 --- a/Exiled.CustomModules/API/Features/CustomGamemodes/World.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Exiled Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.CustomModules.API.Features -{ - using Exiled.API.Features.Core.Generics; - - /// - /// The world base. - /// - public class World : StaticActor - { - private GameState gameState; - - /// - /// Gets the . - /// - public GameState GameState => gameState ??= GetComponent(); - } -} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs b/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs index 807c81621c..abb6dad8f8 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs @@ -20,10 +20,11 @@ namespace Exiled.CustomModules.API.Features.CustomItems using Exiled.API.Features.Items; using Exiled.API.Features.Pickups; using Exiled.API.Features.Spawn; + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.Attributes; using Exiled.CustomModules.API.Features.CustomEscapes; using Exiled.CustomModules.API.Features.CustomItems.Items; using MapGeneration.Distributors; - using UnityEngine; /// @@ -126,6 +127,13 @@ public abstract class CustomItem : CustomModule, IAdditiveBehaviour /// public IEnumerable Items => ItemsValue.Where(x => x.Value.Id == Id).Select(x => x.Key); + /// + /// Gets a based on the provided id or . + /// + /// The id or of the custom item. + /// The with the specified id, or if no item is found. + public static CustomItem Get(object id) => id is uint or UUCustomItemType ? Get((uint)id) : null; + /// /// Retrieves a instance based on the specified custom item id. /// @@ -189,6 +197,14 @@ public static CustomItem Get(Pickup item) return customItem; } + /// + /// Attempts to retrieve a based on the provided id or . + /// + /// The id or of the custom item. + /// When this method returns, contains the associated with the specified id, if the id was found; otherwise, . + /// if a was found; otherwise, . + public static bool TryGet(object id, out CustomItem customItem) => customItem = Get(id); + /// /// Tries to retrieve a instance based on the specified custom item id. /// @@ -348,10 +364,27 @@ public static bool TryGive(Player player, Type type, bool displayMessage = true) /// must be marked with the to be considered for enabling. If /// a custom item is enabled successfully, it is added to the returned list. /// - public static List EnableAll() + public static List EnableAll() => EnableAll(Assembly.GetCallingAssembly()); + + /// + /// Enables all the custom items present in the assembly. + /// + /// The assembly to enable the items from. + /// + /// A of containing all the enabled custom items. + /// + /// + /// This method dynamically enables all custom items found in the calling assembly. Custom items + /// must be marked with the to be considered for enabling. If + /// a custom item is enabled successfully, it is added to the returned list. + /// + public static List EnableAll(Assembly assembly) { + if (!CustomModules.Instance.Config.Modules.Contains(ModuleType.CustomItems)) + throw new Exception("ModuleType::CustomItems must be enabled in order to load any custom items"); + List customItems = new(); - foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) + foreach (Type type in assembly.GetTypes()) { CustomItemAttribute attribute = type.GetCustomAttribute(); if (!typeof(CustomItem).IsAssignableFrom(type) || attribute is null) diff --git a/Exiled.CustomModules/API/Features/CustomItems/Generics/CustomItem.cs b/Exiled.CustomModules/API/Features/CustomItems/Generic/CustomItem.cs similarity index 100% rename from Exiled.CustomModules/API/Features/CustomItems/Generics/CustomItem.cs rename to Exiled.CustomModules/API/Features/CustomItems/Generic/CustomItem.cs diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Armors/ArmorBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Armors/ArmorBehaviour.cs index 563bf63dcf..feeb2833a8 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/Items/Armors/ArmorBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Armors/ArmorBehaviour.cs @@ -8,7 +8,7 @@ namespace Exiled.CustomModules.API.Features.CustomItems.Items.Armors { using Exiled.API.Features; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Items; using Exiled.CustomModules.API.Features.CustomItems.Items; diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/BaseCandy.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/BaseCandy.cs new file mode 100644 index 0000000000..d06da0e36a --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/BaseCandy.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.CustomItems.Items.Candies +{ + using System; + + using Exiled.API.Features; + using InventorySystem.Items.Usables.Scp330; + + /// + /// A class that converts custom candy to base game candy. + /// + internal class BaseCandy : ICandy + { + private CandySettings settings; + + /// + /// Initializes a new instance of the class. + /// + /// The that will be encapsulated. + /// The delegate to be passed to . + internal BaseCandy(CandySettings settings, Action applyEffectsDelegate) + { + this.settings = settings; + } + + /// + public CandyKindID Kind => settings.CandyType; + + /// + public float SpawnChanceWeight => settings.Weight / 100; + + /// + /// Gets the delegate to be passed to . + /// + public Action ApplyEffects { get; } + + /// + public void ServerApplyEffects(ReferenceHub hub) => ApplyEffects(Player.Get(hub).Cast()); + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/CandyBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/CandyBehaviour.cs new file mode 100644 index 0000000000..a4e5ab7fd1 --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/CandyBehaviour.cs @@ -0,0 +1,159 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.CustomItems.Items.Candies +{ + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Features; + using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; + using Exiled.API.Features.Core.Generic; + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Scp330; + using UnityEngine; + + /// + /// Represents the base class for custom candies behaviors. + /// + /// + /// This class extends . + ///
It provides a foundation for creating custom behaviors associated with in-game candies. + ///
+ public abstract class CandyBehaviour : ItemBehaviour + { + /// + /// Gets or sets a containing all tracked indexes in . + /// + public HashSet TrackedCandies { get; protected set; } + + /// + public CandySettings CandySettings => Settings.Cast(); + + /// . + public Scp330 Scp330 => Owner.Cast(); + + /// + /// Applies effect to player. + /// + /// The player to apply effects to. + public virtual void ApplyEffects(Pawn player) + { + } + + /// + protected override void PostInitialize() + { + base.PostInitialize(); + + if (Owner is not Scp330 _) + { + Log.Debug($"{CustomItem.Name} is not a Scp330 bag!", true); + Destroy(); + } + + if (Settings is not CandySettings _) + { + Log.Debug($"{CustomItem.Name} settings is not suitable for candies!", true); + Destroy(); + } + + CandySettings.SelectedText = new($"Custom candies in this bag:\n{string.Join("\n", TrackedCandies.Select(x => x++))}"); + TrackedCandies = new(); + } + + /// + protected override void OnDestroyed() + { + base.OnDestroyed(); + + TrackedCandies.Clear(); + } + + /// + protected override bool Check(Item owner) => base.Check(owner) && owner.Is(out Scp330 scp330) && TrackedCandies.Contains(scp330.SelectedCandyId); + + /// + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + + Exiled.Events.Handlers.Scp330.EatingScp330 += OnEatingScp330Internal; + Exiled.Events.Handlers.Scp330.InteractingScp330 += OnInteractingScp330Internal; + } + + /// + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + + Exiled.Events.Handlers.Scp330.EatingScp330 -= OnEatingScp330Internal; + Exiled.Events.Handlers.Scp330.InteractingScp330 -= OnInteractingScp330Internal; + } + + /// + protected override void OnAcquired(Player player, Item item, bool displayMessage = true) + { + base.OnAcquired(player, item, displayMessage); + + if (Scp330.Candies.Count == 0) + { + TrackedCandies.Add(Scp330.Candies.Count); + Scp330.AddCandy(CandySettings.CandyType); + } + else + { + TrackedCandies.Add(Scp330.Candies.Count - 1); + } + } + + /// + /// Fired when player is eating custom candy. + /// + /// The event instance. + protected virtual void OnEatingCandy(EatingScp330EventArgs ev) + { + } + + /// + /// Fired when player interacts with SCP-330 and gets a custom candy. + /// + /// The event instance. + protected virtual void OnInteracting(InteractingScp330EventArgs ev) + { + } + + /// + private protected void OnEatingScp330Internal(EatingScp330EventArgs ev) + { + if (!ev.Player.TryGetItems(x => x.Type == ItemType.SCP330, out IEnumerable items) || !items.Single().Is(out Scp330 scp330) || !Check(scp330)) + return; + + ev.Candy = new BaseCandy(CandySettings, ApplyEffects); + ev.Player.ShowTextDisplay(CandySettings.EatenCustomCandyMessage); + + OnEatingCandy(ev); + } + + /// + private protected void OnInteractingScp330Internal(InteractingScp330EventArgs ev) + { + if (ev.Candy != CandySettings.CandyType || Random.value * 100 >= CandySettings.Weight || + !ev.Player.TryGetItems(x => x.Type == ItemType.SCP330, out IEnumerable items) || + !items.Single().Is(out Scp330 scp330)) + return; + + TrackedCandies.Add(scp330.SelectedCandyId); + StaticActor.Get().AddOrTrack(scp330); + scp330.AddComponent(); + ev.Player.ShowTextDisplay(CandySettings.ReceivedCustomCandyMessage); + + OnInteracting(ev); + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/CandySettings.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/CandySettings.cs new file mode 100644 index 0000000000..cb50373280 --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/CandySettings.cs @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.CustomItems.Items.Candies +{ + using System; + + using Exiled.API.Enums; + using Exiled.API.Features; + using InventorySystem.Items.Usables.Scp330; + using UnityEngine; + + /// + /// A tool to easily setup candies. + /// + public class CandySettings : ItemSettings + { + /// + public override ItemType ItemType + { + get => base.ItemType; + set + { + if (value != ItemType.SCP330) + throw new ArgumentOutOfRangeException(nameof(Type), value, "ItemType must be ItemType.SCP330"); + + base.ItemType = value; + } + } + + /// + /// Gets or sets a of a custom candy. + /// + public virtual CandyKindID CandyType { get; set; } + + /// + /// Gets or sets chance that player would get a custom candy. + /// + public override float Weight + { + get => base.Weight; + set => base.Weight = Mathf.Clamp(value, 0, 100); + } + + /// + /// Gets or sets the that will be displayed when player ate custom candy.. + /// + public virtual TextDisplay EatenCustomCandyMessage { get; set; } + + /// + /// Gets or sets a that will be displayed when player has received custom candy. + /// + public virtual TextDisplay ReceivedCustomCandyMessage { get; set; } + + /// + public override TextDisplay SelectedText { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/Patches/DroppingCandy.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/Patches/DroppingCandy.cs new file mode 100644 index 0000000000..07e79e68ee --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Candies/Patches/DroppingCandy.cs @@ -0,0 +1,57 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.CustomItems.Items.Candies.Patches +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Core.Generic.Pools; + using Exiled.API.Features.Items; + using HarmonyLib; + using InventorySystem.Items; + using InventorySystem.Items.Usables.Scp330; + + using static HarmonyLib.AccessTools; + + /// + /// Patches to add custom candies tracking-removing logic. + /// + [HarmonyPatch(typeof(Scp330NetworkHandler), nameof(Scp330NetworkHandler.ServerSelectMessageReceived))] + internal class DroppingCandy + { + private static IEnumerable Transpiler(IEnumerable instructions) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ldloca_S); + + newInstructions.InsertRange( + index, + new[] + { + // DropCandy(Item, SelectScp330Message); + new CodeInstruction(OpCodes.Ldloc_1).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Item), nameof(Item.Get), new[] { typeof(ItemBase) })), + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(DroppingCandy), nameof(DropCandy))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + } + + private static void DropCandy(Item item, SelectScp330Message msg) + { + if (!item.Is(out Scp330 _) || !CustomItem.TryGet(item, out CustomItem customItem) + || customItem.BehaviourComponent != typeof(CandyBehaviour) || !item.TryGetComponent(out CandyBehaviour candyBehaviour)) + return; + + candyBehaviour.TrackedCandies.Remove(msg.CandyID); + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Explosives/GrenadeBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Explosives/GrenadeBehaviour.cs index 72768c3c33..73091af733 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/Items/Explosives/GrenadeBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Explosives/GrenadeBehaviour.cs @@ -9,7 +9,7 @@ namespace Exiled.CustomModules.API.Features.CustomItems.Items.Explosives { using Exiled.API.Features; using Exiled.API.Features.Components; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Items; using Exiled.API.Features.Pickups; using Exiled.API.Features.Pickups.Projectiles; diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/FirearmBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/FirearmBehaviour.cs index 215626e71c..82ac5c8445 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/FirearmBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/FirearmBehaviour.cs @@ -13,7 +13,7 @@ namespace Exiled.CustomModules.API.Features.CustomItems.Items.Firearms using Exiled.API.Enums; using Exiled.API.Extensions; using Exiled.API.Features; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Items; using Exiled.CustomModules.API.Features.CustomItems.Items; using Exiled.Events.EventArgs.Item; diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/SemiAutomaticFirearmBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/SemiAutomaticFirearmBehaviour.cs index d1942167ca..9e425981a7 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/SemiAutomaticFirearmBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/Firearms/SemiAutomaticFirearmBehaviour.cs @@ -9,7 +9,8 @@ namespace Exiled.CustomModules.API.Features.CustomItems.Items.Firearms { using System.Collections.Generic; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Behaviours; + using Exiled.API.Features.Core.Generic; using Exiled.API.Features.Items; using Exiled.CustomModules.API.Enums; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.CustomModules/API/Features/CustomItems/Items/ItemBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Items/ItemBehaviour.cs index cc399dcb54..f97508312f 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/Items/ItemBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/Items/ItemBehaviour.cs @@ -15,6 +15,7 @@ namespace Exiled.CustomModules.API.Features.CustomItems.Items using Exiled.API.Features; using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; using Exiled.API.Features.Items; diff --git a/Exiled.CustomModules/API/Features/CustomItems/Pickups/PickupBehaviour.cs b/Exiled.CustomModules/API/Features/CustomItems/Pickups/PickupBehaviour.cs index c083a55e90..e65ce86d37 100644 --- a/Exiled.CustomModules/API/Features/CustomItems/Pickups/PickupBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomItems/Pickups/PickupBehaviour.cs @@ -14,6 +14,7 @@ namespace Exiled.CustomModules.API.Features.CustomItems.Pickups using Exiled.API.Features; using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; using Exiled.API.Features.Pickups; diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs index f754df82e4..b70a0710f1 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomRole.cs @@ -15,12 +15,15 @@ namespace Exiled.CustomModules.API.Features.CustomRoles using Exiled.API.Enums; using Exiled.API.Extensions; using Exiled.API.Features; + using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; using Exiled.API.Features.Core.Interfaces; + using Exiled.API.Features.DynamicEvents; + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.Attributes; using Exiled.CustomModules.API.Features.CustomEscapes; - + using Exiled.CustomModules.Events.EventArgs.CustomRoles; using MEC; - using PlayerRoles; using Respawning; @@ -43,6 +46,18 @@ public abstract class CustomRole : CustomModule, IAdditiveBehaviour private static readonly Dictionary IdLookupTable = new(); private static readonly Dictionary NameLookupTable = new(); + /// + /// Gets or sets the which handles all delegates to be fired before a player changes role. + /// + [DynamicEventDispatcher] + public static TDynamicEventDispatcher ChangingCustomRoleDispatcher { get; protected set; } + + /// + /// Gets or sets the which handles all delegates to be fired after a player changes role. + /// + [DynamicEventDispatcher] + public static TDynamicEventDispatcher ChangedCustomRoleDispatcher { get; protected set; } + /// /// Gets a which contains all registered 's. /// @@ -81,8 +96,13 @@ public abstract class CustomRole : CustomModule, IAdditiveBehaviour /// /// Gets a value indicating whether a player can spawn as this based on its assigned probability. /// - /// if the probability condition was satified; otherwise, . - public bool CanSpawnByProbability => UnityEngine.Random.Range(0, 101) <= Chance; + /// if the probability condition was satisfied; otherwise, . + public bool CanSpawnByProbability => UnityEngine.Random.Range(0, 101) <= Probability; + + /// + /// Gets all instances of this . + /// + public int Instances { get; private set; } /// /// Gets the 's description. @@ -95,14 +115,81 @@ public abstract class CustomRole : CustomModule, IAdditiveBehaviour public virtual RoleTypeId Role { get; } /// - /// Gets a value indicating whether the should be considered an SCP. + /// Gets the relative spawn chance of the . /// - public virtual bool IsScp { get; } + public virtual int Probability { get; } /// - /// Gets the relative spawn chance of the . + /// Gets a value indicating whether the role can spawn given a condition. + /// + public virtual bool EvaluateConditions + { + get + { + IEnumerable list = Player.List.Cast(); + + if (RequiredTeamToSpawn is not Team.Dead) + { + foreach (Pawn pawn in list) + { + if ((!pawn.HasCustomRole || !pawn.CustomRole.TeamsOwnership.Contains(RequiredTeamToSpawn)) && pawn.Role.Team != RequiredTeamToSpawn) + continue; + + return true; + } + } + + if (RequiredRoleToSpawn is not RoleTypeId.None) + { + foreach (Pawn pawn in list) + { + if (pawn.Role == RequiredRoleToSpawn) + { + if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs && !pawn.IsScp) || + (RoleExtensions.GetTeam(RequiredRoleToSpawn) is not Team.SCPs && pawn.IsScp)) + continue; + + return true; + } + } + } + + return (RequiredCustomTeamToSpawn > 0 && CustomTeam.TryGet(RequiredCustomTeamToSpawn, out CustomTeam team) && !team.Owners.IsEmpty()) || + (RequiredCustomRoleToSpawn > 0 && CustomRole.TryGet(RequiredCustomRoleToSpawn, out CustomRole role) && !role.Owners.IsEmpty()); + } + } + + /// + /// Gets the required that players must belong to in order to allow the to spawn. /// - public virtual int Chance { get; } + /// + /// This property specifies the required alive team to be eligible for spawning in the . + /// + public virtual Team RequiredTeamToSpawn => Team.Dead; + + /// + /// Gets the required that players must have to allow the to spawn. + /// + /// + /// This property specifies the required role type for players to be eligible for spawning in the . + /// + public virtual RoleTypeId RequiredRoleToSpawn => RoleTypeId.None; + + /// + /// Gets the required custom team that players must belong to in order to allow the to spawn. + /// + /// + /// This property specifies the required alive custom team to be eligible for spawning in the . + /// + public virtual uint RequiredCustomTeamToSpawn { get; } + + /// + /// Gets the required that players must have to allow the to spawn. + /// + /// + /// This property specifies the required custom role for players to be eligible for spawning in the . + /// + public virtual uint RequiredCustomRoleToSpawn { get; } /// /// Gets the . @@ -124,6 +211,14 @@ public abstract class CustomRole : CustomModule, IAdditiveBehaviour /// public virtual int MaxInstances => IsScp ? 1 : -1; + /// + /// Gets the required teams for this to win. + /// + /// + /// This property specifies the teams the belongs to. + /// + public virtual Team[] TeamsOwnership { get; } = { }; + /// /// Gets the from which to retrieve players for assigning the . /// @@ -134,21 +229,38 @@ public abstract class CustomRole : CustomModule, IAdditiveBehaviour /// public virtual RoleTypeId AssignFromRole { get; } + /// + /// Gets all roles to override, preventing the specified roles to spawn. + /// + public virtual RoleTypeId[] OverrideScps { get; } + /// /// Gets a value indicating whether the should be treated as a separate team unit. /// - public virtual bool IsCustomTeamUnit { get; } + public virtual bool IsTeamUnit { get; } /// /// Gets a value indicating whether the is registered. /// public virtual bool IsRegistered => Registered.Contains(this); + /// + /// Gets a value indicating whether the should be considered an SCP. + /// + public virtual bool IsScp { get; } + /// /// Gets a of containing all players owning this . /// public IEnumerable Owners => Player.Get(x => TryGet(x.Cast(), out CustomRole customRole) && customRole.Id == Id).Cast(); + /// + /// Gets a based on the provided id or . + /// + /// The id or of the custom role. + /// The with the specified id, or if no role is found. + public static CustomRole Get(object id) => id is uint or UUCustomRoleType ? Get((uint)id) : null; + /// /// Gets a given the specified id. /// @@ -172,6 +284,49 @@ public static CustomRole Get(Type type) => typeof(CustomRole).IsAssignableFrom(type) ? TypeLookupTable[type] : typeof(RoleBehaviour).IsAssignableFrom(type) ? BehaviourLookupTable[type] : null; + /// + /// Gets all instances based on the predicate. + /// + /// The predicate. + /// All instances matching the predicate. + public static IEnumerable Get(Func predicate) => List.Where(predicate); + + /// + /// Gets all instances belonging to the specified . + /// + /// The specified . + /// All instances belonging to the specified . + public static IEnumerable Get(RoleTypeId role) => List.Where(customRole => customRole.AssignFromRole == role); + + /// + /// Gets all instances belonging to the specified . + /// + /// The specified . + /// All instances belonging to the specified . + public static IEnumerable Get(SpawnableTeamType team) => List.Where(customRole => customRole.AssignFromTeam == team); + + /// + /// Gets all instances belonging to the specified . + /// + /// The specified . + /// All instances belonging to the specified . + public static IEnumerable Get(Team team) => List.Where(customRole => RoleExtensions.GetTeam(customRole.Role) == team || customRole.TeamsOwnership.Contains(team)); + + /// + /// Gets all instances belonging to the specified teams. + /// + /// The specified teams. + /// All instances belonging to the specified teams. + public static IEnumerable Get(IEnumerable teams) => List.Where(customRole => + teams.Contains(RoleExtensions.GetTeam(customRole.Role)) || customRole.TeamsOwnership.Any(to => teams.Contains(to))); + + /// + /// Gets all instances belonging to the specified teams. + /// + /// The specified teams. + /// All instances belonging to the specified teams. + public static IEnumerable Get(params Team[] teams) => List.Where(customRole => teams.Contains(RoleExtensions.GetTeam(customRole.Role))); + /// /// Gets a given the specified . /// @@ -187,6 +342,14 @@ public static CustomRole Get() /// The matching the search or if not registered. public static CustomRole Get(Pawn player) => PlayersValue.TryGetValue(player, out CustomRole customRole) ? customRole : default; + /// + /// Attempts to retrieve a based on the provided id or . + /// + /// The id or of the custom role. + /// When this method returns, contains the associated with the specified id, if the id was found; otherwise, . + /// if a was found; otherwise, . + public static bool TryGet(object id, out CustomRole customRole) => customRole = Get(id); + /// /// Tries to get a given the specified id. /// @@ -423,10 +586,27 @@ public static void Remove(IEnumerable players) /// must be marked with the to be considered for enabling. If /// a custom role is enabled successfully, it is added to the returned list. /// - public static List EnableAll() + public static List EnableAll() => EnableAll(Assembly.GetCallingAssembly()); + + /// + /// Enables all the custom roles present in the assembly. + /// + /// The assembly to enable the roles from. + /// + /// A of containing all the enabled custom roles. + /// + /// + /// This method dynamically enables all custom roles found in the calling assembly. Custom roles + /// must be marked with the to be considered for enabling. If + /// a custom role is enabled successfully, it is added to the returned list. + /// + public static List EnableAll(Assembly assembly) { + if (!CustomModules.Instance.Config.Modules.Contains(ModuleType.CustomRoles)) + throw new Exception("ModuleType::CustomRoles must be enabled in order to load any custom roles"); + List customRoles = new(); - foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) + foreach (Type type in assembly.GetTypes()) { CustomRoleAttribute attribute = type.GetCustomAttribute(); if (!typeof(CustomRole).IsAssignableFrom(type) || attribute is null) @@ -484,9 +664,33 @@ public bool Spawn(Pawn player) if (player.IsAlive) return false; + ChangingCustomRoleEventArgs ev = new(player, Id); + ChangingCustomRoleDispatcher.InvokeAll(ev); + + if (!ev.IsAllowed) + return false; + + player = ev.Player.Cast(); + if (ev.Role is RoleTypeId rId) + { + player.SetRole(rId); + return true; + } + + if (!CustomRole.TryGet(ev.Role, out CustomRole role)) + return false; + + if (role.Id != Id) + return role.Spawn(player); + + object prevRole = player.CustomRole ? player.CustomRole.Id : player.Role.Type; player.AddComponent(BehaviourComponent); PlayersValue.Remove(player); PlayersValue.Add(player, this); + Instances += 1; + + ChangedCustomRoleEventArgs @event = new(player, prevRole); + ChangedCustomRoleDispatcher.InvokeAll(@event); return true; } @@ -514,17 +718,47 @@ public bool Spawn(Pawn player) /// public void ForceSpawn(Pawn player) { + ChangingCustomRoleEventArgs ev = new(player, Id); + ChangingCustomRoleDispatcher.InvokeAll(ev); + + if (!ev.IsAllowed) + return; + + player = ev.Player.Cast(); + if (ev.Role is RoleTypeId rId) + { + player.SetRole(rId); + return; + } + + if (!CustomRole.TryGet(ev.Role, out CustomRole role)) + return; + + if (role.Id != Id) + { + role.ForceSpawn(player); + return; + } + + object prevRole = player.CustomRole ? player.CustomRole.Id : player.Role.Type; Remove(player); PlayersValue.Add(player, this); if (!player.IsAlive) { - ForceSpawn_Internal(player); + ForceSpawn_Internal(player, false); + ChangedCustomRoleEventArgs @event = new(player, prevRole); + ChangedCustomRoleDispatcher.InvokeAll(@event); return; } player.Role.Set(RoleTypeId.Spectator, SpawnReason.Respawn); - Timing.CallDelayed(0.1f, () => ForceSpawn_Internal(player)); + Timing.CallDelayed(0.1f, () => + { + ForceSpawn_Internal(player, false); + ChangedCustomRoleEventArgs @event = new(player, prevRole); + ChangedCustomRoleDispatcher.InvokeAll(@event); + }); } /// @@ -539,17 +773,47 @@ public void ForceSpawn(Pawn player) /// public void ForceSpawn(Pawn player, bool preservePosition) { + ChangingCustomRoleEventArgs ev = new(player, Id); + ChangingCustomRoleDispatcher.InvokeAll(ev); + + if (!ev.IsAllowed) + return; + + player = ev.Player.Cast(); + if (ev.Role is RoleTypeId rId) + { + player.SetRole(rId); + return; + } + + if (!CustomRole.TryGet(ev.Role, out CustomRole role)) + return; + + if (role.Id != Id) + { + role.ForceSpawn(player, preservePosition); + return; + } + + object prevRole = player.CustomRole ? player.CustomRole.Id : player.Role.Type; PlayersValue.Remove(player); PlayersValue.Add(player, this); if (!player.IsAlive) { ForceSpawn_Internal(player, preservePosition); + ChangedCustomRoleEventArgs @event = new(player, prevRole); + ChangedCustomRoleDispatcher.InvokeAll(@event); return; } player.Role.Set(RoleTypeId.Spectator, SpawnReason.Respawn); - Timing.CallDelayed(0.1f, () => ForceSpawn_Internal(player, preservePosition)); + Timing.CallDelayed(0.1f, () => + { + ForceSpawn_Internal(player, preservePosition); + ChangedCustomRoleEventArgs @event = new(player, prevRole); + ChangedCustomRoleDispatcher.InvokeAll(@event); + }); } /// @@ -588,7 +852,9 @@ public void ForceSpawn(Pawn player, bool preservePosition) /// public bool Eject(Pawn player) { + Round.IgnoredPlayers.Remove(player); PlayersValue.Remove(player); + Instances -= 1; if (CustomTeam.TryGet(player, out CustomTeam customTeam)) { @@ -678,10 +944,10 @@ internal bool TryUnregister() return true; } - private void ForceSpawn_Internal(Pawn player) => - player.AddComponent(BehaviourComponent, $"ECS-{Name}"); - - private void ForceSpawn_Internal(Pawn player, bool preservePosition) => + private void ForceSpawn_Internal(Pawn player, bool preservePosition) + { + Instances += 1; player.AddComponent(BehaviourComponent, $"ECS-{Name}").Cast().Settings.PreservePosition = preservePosition; + } } } diff --git a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs index 4083ab04cb..3ce45a322d 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/CustomTeam.cs @@ -15,7 +15,9 @@ namespace Exiled.CustomModules.API.Features.CustomRoles using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Core; - + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.Attributes; + using HarmonyLib; using PlayerRoles; using Respawning; using Respawning.NamingRules; @@ -30,7 +32,7 @@ namespace Exiled.CustomModules.API.Features.CustomRoles ///
It serves as a versatile framework for handling custom team-related functionalities and interactions. /// /// - public abstract class CustomTeam : TypeCastObject, IEquatable, IEquatable + public abstract class CustomTeam : CustomModule { private static readonly Dictionary PlayersValue = new(); private static readonly List Registered = new(); @@ -56,38 +58,19 @@ public abstract class CustomTeam : TypeCastObject, IEquatable Players => PlayersValue.Keys.ToHashSet(); /// - /// Gets or sets a collection of instances representing all custom role types offered as units. + /// Gets the name of the . /// - /// - /// This property provides access to a curated collection of objects, encapsulating all available custom role types within the context of units. - ///
The collection is designed to be both queried and modified as needed to accommodate dynamic scenarios within the game architecture. - ///
- public virtual IEnumerable Units { get; protected set; } = new Type[] { }; + public override string Name { get; } /// /// Gets or sets the 's id. /// - public virtual uint Id { get; protected set; } - - /// - /// Gets the relative spawn probability of the . - /// - public virtual int Probability { get; } - - /// - /// Gets the which is being spawned from. - /// - public virtual SpawnableTeamType RespawnTeam { get; } - - /// - /// Gets the name of the . - /// - public abstract string Name { get; } + public override uint Id { get; protected set; } /// /// Gets a value indicating whether the is enabled. /// - public virtual bool IsEnabled => true; + public override bool IsEnabled => true; /// /// Gets the display name of the . @@ -111,7 +94,49 @@ public abstract class CustomTeam : TypeCastObject, IEquatable /// The size indicates the maximum number of players that can be part of this . /// - public virtual uint Size { get; } + public virtual int Size { get; } + + /// + /// Gets or sets a collection of ids representing all custom roles offered as units. + /// + /// + /// This property provides access to a curated collection of objects, encapsulating all available custom role within the context of units. + ///
The collection is designed to be both queried and modified as needed to accommodate dynamic scenarios within the game architecture. + ///
+ public virtual IEnumerable Units { get; protected set; } = new uint[] { }; + + /// + /// Gets the amount of time after which any team will be allowed to spawn. + /// + public virtual float NextSequenceTime => UnityEngine.Random.Range( + GameCore.ConfigFile.ServerConfig.GetFloat("minimum_MTF_time_to_spawn", MinNextSequenceTime), + GameCore.ConfigFile.ServerConfig.GetFloat("maximum_MTF_time_to_spawn", MaxNextSequenceTime)); + + /// + /// Gets or sets the minimum amount time after which any team will be allowed to spawn. + /// + public virtual float MinNextSequenceTime { get; set; } = 280f; + + /// + /// Gets or sets the maximum amount time after which any team will be spawned. + /// + public virtual float MaxNextSequenceTime { get; set; } = 350f; + + /// + /// Gets the relative spawn probability of the . + /// + public virtual int Probability { get; } + + /// + /// Gets a value indicating whether a player can spawn as this based on its assigned probability. + /// + /// if the probability condition was satisfied; otherwise, . + public bool CanSpawnByProbability => UnityEngine.Random.Range(0, 101) <= Probability; + + /// + /// Gets the which is being spawned from. + /// + public virtual SpawnableTeamType[] SpawnableFromTeams { get; } /// /// Gets a value indicating whether the is configured to use respawn tickets. @@ -130,12 +155,52 @@ public abstract class CustomTeam : TypeCastObject, IEquatable - /// Gets a value indicating whether the can be spawned even when SCP entities are alive. + /// Gets a value indicating whether the team can spawn given a condition. + /// + public virtual bool EvaluateConditions + { + get + { + IEnumerable list = Player.List.Cast(); + + if (RequiredTeamToSpawn is not Team.Dead) + { + foreach (Pawn pawn in list) + { + if ((!pawn.HasCustomRole || !pawn.CustomRole.TeamsOwnership.Contains(RequiredTeamToSpawn)) && pawn.Role.Team != RequiredTeamToSpawn) + continue; + + return true; + } + } + + if (RequiredRoleToSpawn is not RoleTypeId.None) + { + foreach (Pawn pawn in list) + { + if (pawn.Role == RequiredRoleToSpawn) + { + if ((RoleExtensions.GetTeam(RequiredRoleToSpawn) is Team.SCPs && !pawn.IsScp) || + (RoleExtensions.GetTeam(RequiredRoleToSpawn) is not Team.SCPs && pawn.IsScp)) + continue; + + return true; + } + } + } + + return (RequiredCustomTeamToSpawn > 0 && CustomTeam.TryGet(RequiredCustomTeamToSpawn, out CustomTeam team) && !team.Owners.IsEmpty()) || + (RequiredCustomRoleToSpawn > 0 && CustomRole.TryGet(RequiredCustomRoleToSpawn, out CustomRole role) && !role.Owners.IsEmpty()); + } + } + + /// + /// Gets the required that players must belong to in order to allow the to spawn. /// /// - /// If set to true, the can spawn even when SCP entities are alive. + /// This property specifies the required alive team to be eligible for spawning in the . /// - public virtual bool CanSpawnWithoutScps => true; + public virtual Team RequiredTeamToSpawn => Team.Dead; /// /// Gets the required that players must have to allow the to spawn. @@ -145,6 +210,14 @@ public abstract class CustomTeam : TypeCastObject, IEquatable public virtual RoleTypeId RequiredRoleToSpawn => RoleTypeId.None; + /// + /// Gets the required custom team that players must belong to in order to allow the to spawn. + /// + /// + /// This property specifies the required alive custom team to be eligible for spawning in the . + /// + public virtual uint RequiredCustomTeamToSpawn { get; } + /// /// Gets the required that players must have to allow the to spawn. /// @@ -154,12 +227,9 @@ public abstract class CustomTeam : TypeCastObject, IEquatable - /// Gets the required leading teams for this to win. + /// Gets the teams the belongs to. /// - /// - /// This property specifies the teams that the must surpass to achieve victory. - /// - public virtual Team[] LeadingTeamsToWin => new Team[] { }; + public virtual Team[] TeamsOwnership { get; } = { }; /// /// Gets a value indicating whether the is registered. @@ -186,52 +256,47 @@ public abstract class CustomTeam : TypeCastObject, IEquatable CustomRole.Get(Units.Random()); /// - /// Compares two operands: and . + /// Gets all instances based on the predicate. /// - /// The to compare. - /// The to compare. - /// if the values are equal. - public static bool operator ==(CustomTeam left, object right) => left is null ? right is null : left.Equals(right); + /// The predicate. + /// All instances matching the predicate. + public static IEnumerable Get(Func predicate) => List.Where(predicate); /// - /// Compares two operands: and . + /// Gets a based on the provided id or . /// - /// The to compare. - /// The to compare. - /// if the values are not equal. - public static bool operator ==(object left, CustomTeam right) => right == left; + /// The id or of the custom team. + /// The with the specified id, or if no team is found. + public static CustomTeam Get(object id) => id is uint or UUCustomTeamType ? Get((uint)id) : null; /// - /// Compares two operands: and . + /// Gets a instance based on the specified id. /// - /// The to compare. - /// The to compare. - /// if the values are not equal. - public static bool operator !=(CustomTeam left, object right) => !(left == right); + /// The id to check. + /// The matching the search, or if not registered. + public static CustomTeam Get(uint id) => IdLookupTable[id]; /// - /// Compares two operands: and . + /// Gets a instance based on the specified name. /// - /// The left to compare. - /// The right to compare. - /// if the values are not equal. - public static bool operator !=(object left, CustomTeam right) => !(left == right); + /// The specified name. + /// The matching the search, or if not registered. + public static CustomTeam Get(string name) => NameLookupTable[name]; /// - /// Compares two operands: and . + /// Gets a instance associated with a specific . /// - /// The left to compare. - /// The right to compare. - /// if the values are equal. - public static bool operator ==(CustomTeam left, CustomTeam right) => left is null ? right is null : left.Equals(right); + /// The to check. + /// The matching the search, or if not registered. + public static CustomTeam Get(Player player) => !PlayersValue.TryGetValue(player, out CustomTeam customTeam) ? null : customTeam; /// - /// Compares two operands: and . + /// Attempts to retrieve a based on the provided id or . /// - /// The left to compare. - /// The right to compare. - /// if the values are not equal. - public static bool operator !=(CustomTeam left, CustomTeam right) => !(left.Id == right.Id); + /// The id or of the custom team. + /// When this method returns, contains the associated with the specified id, if the id was found; otherwise, . + /// if a was found; otherwise, . + public static bool TryGet(object id, out CustomTeam customTeam) => customTeam = Get(id); /// /// Tries to get a given the specified id. @@ -261,8 +326,9 @@ public abstract class CustomTeam : TypeCastObject, IEquatable. /// /// The to be spawned. + /// Forces the respawn wave regardless any conditions, including tickets. /// if the was successfully spawned; otherwise, . - public static bool TrySpawn(CustomTeam customTeam) + public static bool TryRespawn(CustomTeam customTeam, bool isForced = false) { if (!Player.Get(p => p.IsDead).Any() || customTeam is null) return false; @@ -275,8 +341,9 @@ public static bool TrySpawn(CustomTeam customTeam) /// Tries to spawn a given the specified id. /// /// The specified id. + /// Forces the respawn wave regardless any conditions, including tickets. /// if the was successfully spawned; otherwise, . - public static bool TrySpawn(uint id) + public static bool TryRespawn(uint id, bool isForced = false) { if (!Player.Get(p => p.IsDead).Any() || TryGet(id, out CustomTeam customTeam)) return false; @@ -290,8 +357,9 @@ public static bool TrySpawn(uint id) ///
/// The to be spawned. /// The unit to be assigned. + /// Forces the respawn wave regardless any conditions, including tickets. /// if the player was successfully spawned; otherwise, . - public static bool TrySpawn(Pawn player, CustomTeam customTeam) + public static bool TrySpawn(Pawn player, CustomTeam customTeam, bool isForced = false) { if (customTeam is null) return false; @@ -351,7 +419,7 @@ public static bool TrySpawn(IEnumerable players, uint id) /// The amount of units to be spawned. /// The to be spawned. /// if the was successfully spawned; otherwise, . - public static bool TrySpawn(uint amount, CustomTeam customTeam) + public static bool TrySpawn(int amount, CustomTeam customTeam) { if (customTeam is null) return false; @@ -366,9 +434,9 @@ public static bool TrySpawn(uint amount, CustomTeam customTeam) /// The amount of units to be spawned. /// The specified id. /// if the was successfully spawned; otherwise, . - public static bool TrySpawn(uint amount, uint id) + public static bool TrySpawn(int amount, uint id) { - if (TryGet(id, out CustomTeam customTeam)) + if (!TryGet(id, out CustomTeam customTeam)) return false; customTeam.Respawn(amount); @@ -379,10 +447,20 @@ public static bool TrySpawn(uint amount, uint id) /// Enables all the custom teams present in the assembly. ///
/// A of which contains all the enabled custom teams. - public static IEnumerable EnableAll() + public static IEnumerable EnableAll() => EnableAll(Assembly.GetCallingAssembly()); + + /// + /// Enables all the custom teams present in the assembly. + /// + /// The assembly to enable the teams from. + /// A of which contains all the enabled custom teams. + public static IEnumerable EnableAll(Assembly assembly) { + if (!CustomModules.Instance.Config.Modules.Contains(ModuleType.CustomTeams)) + throw new Exception("ModuleType::CustomTeams must be enabled in order to load any custom teams"); + List customTeams = new(); - foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) + foreach (Type type in assembly.GetTypes()) { CustomTeamAttribute attribute = type.GetCustomAttribute(); if (type.BaseType != typeof(CustomTeam) || attribute is null) @@ -417,27 +495,6 @@ public static IEnumerable DisableAll() return customTeams; } - /// - /// Gets a instance based on the specified id. - /// - /// The id to check. - /// The matching the search, or if not registered. - public static CustomTeam Get(uint id) => IdLookupTable[id]; - - /// - /// Gets a instance based on the specified name. - /// - /// The specified name. - /// The matching the search, or if not registered. - public static CustomTeam Get(string name) => NameLookupTable[name]; - - /// - /// Gets a instance associated with a specific . - /// - /// The to check. - /// The matching the search, or if not registered. - public static CustomTeam Get(Player player) => !PlayersValue.TryGetValue(player, out CustomTeam customTeam) ? null : customTeam; - /// /// Determines whether the provided id is equal to the current object. /// @@ -517,6 +574,7 @@ public bool Eject(Pawn player) /// Forces a respawn wave by spawning the specified amount of units. ///
/// The number of units to be spawned. + /// Forces the respawn wave regardless any conditions, including tickets. /// /// If the provided is less than or equal to zero, no respawn action is taken. /// @@ -526,16 +584,19 @@ public bool Eject(Pawn player) /// Additionally, if the respawn team is of type and a valid is available using , a new unit naming message is sent for NineTailedFox units. /// /// - public void Respawn(uint amount) + public void Respawn(int amount, bool isForced = false) { - Pawn[] players = Player.Get(Team.Dead).Take((int)amount).Cast().ToArray(); + if ((UseTickets && tickets <= 0) && !isForced) + return; + + IEnumerable players = Player.Get(Team.Dead).Take(amount).Cast(); if (players.IsEmpty()) return; players.ForEach(player => Spawn(player)); - if (RespawnTeam is SpawnableTeamType.NineTailedFox && UnitNamingRule.TryGetNamingRule(SpawnableTeamType.NineTailedFox, out UnitNamingRule rule)) + if (TeamsOwnership.Any(team => team == Team.FoundationForces) && UnitNamingRule.TryGetNamingRule(SpawnableTeamType.NineTailedFox, out UnitNamingRule rule)) UnitNameMessageHandler.SendNew(SpawnableTeamType.NineTailedFox, rule); } @@ -544,6 +605,7 @@ public void Respawn(uint amount) ///
/// The collection of players to be spawned. /// A value indicating whether the team size should remain the same as the specified collection. + /// Forces the respawn wave regardless any conditions, including tickets. /// /// If the provided collection of is null or empty, no respawn action is taken. /// @@ -556,9 +618,9 @@ public void Respawn(uint amount) /// Additionally, if the respawn team is of type and a valid is available using , a new unit naming message is sent for NineTailedFox units. /// /// - public void Respawn(IEnumerable players, bool keepSize = true) + public void Respawn(IEnumerable players, bool keepSize = true, bool isForced = false) { - if (players is null || players.IsEmpty()) + if (((UseTickets && tickets <= 0) && !isForced) || players is null || players.IsEmpty()) return; if (UseTickets && tickets > 0) @@ -577,17 +639,18 @@ public void Respawn(IEnumerable players, bool keepSize = true) count++; } - if (RespawnTeam is SpawnableTeamType.NineTailedFox && UnitNamingRule.TryGetNamingRule(SpawnableTeamType.NineTailedFox, out UnitNamingRule rule)) + if (TeamsOwnership.Any(team => team == Team.FoundationForces) && UnitNamingRule.TryGetNamingRule(SpawnableTeamType.NineTailedFox, out UnitNamingRule rule)) UnitNameMessageHandler.SendNew(SpawnableTeamType.NineTailedFox, rule); } /// /// Forces a respawn wave, spawning players up to the specified team size. /// + /// Forces the respawn wave regardless any conditions, including tickets. /// /// This method triggers a respawn wave, spawning players up to the current team size limit. /// - public void Respawn() => Respawn(Size); + public void Respawn(bool isForced = false) => Respawn(Size, isForced); /// /// Adds respawn tickets to the current instance. @@ -608,7 +671,10 @@ public void Respawn(IEnumerable players, bool keepSize = true) public void RemoveTickets(uint amount) { if (tickets < amount) + { + tickets = 0; return; + } tickets -= amount; } @@ -633,7 +699,10 @@ internal bool TryRegister(CustomTeamAttribute attribute = null) } if (attribute.Types is not null && !attribute.Types.IsEmpty()) - Units = Units.Concat(attribute.Types); + { + foreach (Type t in attribute.Types) + Units.AddItem(CustomRole.Get(t).Id); + } } CustomTeam duplicate = Registered.FirstOrDefault(x => x.Id == Id || x.Name == Name); diff --git a/Exiled.CustomModules/API/Features/CustomRoles/Generics/CustomRole.cs b/Exiled.CustomModules/API/Features/CustomRoles/Generic/CustomRole.cs similarity index 100% rename from Exiled.CustomModules/API/Features/CustomRoles/Generics/CustomRole.cs rename to Exiled.CustomModules/API/Features/CustomRoles/Generic/CustomRole.cs diff --git a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs index 8bdedf231e..46640d99bb 100644 --- a/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs +++ b/Exiled.CustomModules/API/Features/CustomRoles/RoleBehaviour.cs @@ -17,10 +17,11 @@ namespace Exiled.CustomModules.API.Features.CustomRoles using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Core; - using Exiled.API.Features.Core.Generics; + using Exiled.API.Features.Core.Behaviours; + using Exiled.API.Features.Core.Generic; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Core.Interfaces; using Exiled.API.Features.DynamicEvents; - using Exiled.API.Features.Pools; using Exiled.API.Features.Roles; using Exiled.API.Features.Spawn; using Exiled.CustomModules.API.Enums; @@ -28,13 +29,9 @@ namespace Exiled.CustomModules.API.Features.CustomRoles using Exiled.CustomModules.API.Features.Inventory; using Exiled.Events.EventArgs.Map; using Exiled.Events.EventArgs.Player; - using PlayerRoles; - using UnityEngine; - using static Exiled.API.Extensions.MirrorExtensions; - /// /// Represents the base class for custom role behaviors. /// @@ -86,6 +83,10 @@ public Vector3 SpawnPoint if (Settings.SpawnProperties is null || Settings.SpawnProperties.IsEmpty) return RoleExtensions.GetRandomSpawnLocation(Role).Position; + return Settings.SpawnProperties.StaticSpawnPoints.Count > 0 && EvalSpawnPoint(Settings.SpawnProperties.StaticSpawnPoints, out Vector3 staticPos) ? staticPos : + Settings.SpawnProperties.DynamicSpawnPoints.Count > 0 && EvalSpawnPoint(Settings.SpawnProperties.DynamicSpawnPoints, out Vector3 dynamicPos) ? dynamicPos : + Settings.SpawnProperties.RoleSpawnPoints.Count > 0 && EvalSpawnPoint(Settings.SpawnProperties.RoleSpawnPoints, out Vector3 rolePos) ? rolePos : Vector3.zero; + static bool EvalSpawnPoint(IEnumerable spawnpoints, out Vector3 outPos) { outPos = default; @@ -101,10 +102,6 @@ static bool EvalSpawnPoint(IEnumerable spawnpoints, out Vector3 outP return false; } - - return Settings.SpawnProperties.StaticSpawnPoints.Count > 0 && EvalSpawnPoint(Settings.SpawnProperties.StaticSpawnPoints, out Vector3 staticPos) ? staticPos : - Settings.SpawnProperties.DynamicSpawnPoints.Count > 0 && EvalSpawnPoint(Settings.SpawnProperties.DynamicSpawnPoints, out Vector3 dynamicPos) ? dynamicPos : - Settings.SpawnProperties.RoleSpawnPoints.Count > 0 && EvalSpawnPoint(Settings.SpawnProperties.RoleSpawnPoints, out Vector3 rolePos) ? rolePos : Vector3.zero; } } @@ -185,6 +182,42 @@ protected virtual RoleTypeId FakeAppearance /// if the specified is ignored; otherwise, . public bool IsDamageIgnored(DamageType damageType) => Settings.IgnoredDamageTypes.Contains(damageType); + /// + /// Evaluates the specified conditions affecting the round's ending conditions. + /// + /// The corresponding evaluation. + public virtual bool EvaluateEndingConditions() + { + if (CustomRole.TeamsOwnership.Length == 1) + return true; + + SummaryInfo summaryInfo = World.Get().SummaryInfo; + + if (CustomRole.TeamsOwnership.Contains(Team.SCPs) && summaryInfo.FoundationForces <= 0 && summaryInfo.ChaosInsurgency <= 0) + return true; + + if (CustomRole.TeamsOwnership.Any(team => team is Team.ClassD or Team.ChaosInsurgency) && summaryInfo.FoundationForces <= 0 && summaryInfo.Anomalies <= 0) + return true; + + if (CustomRole.TeamsOwnership.Any(team => team is Team.FoundationForces or Team.Scientists) && summaryInfo.ChaosInsurgency <= 0 && summaryInfo.Anomalies <= 0) + return true; + + if (CustomRole.TeamsOwnership.IsEmpty()) + { + int uniqueFaction = 0; + if (summaryInfo.FoundationForces > 0) + ++uniqueFaction; + if (summaryInfo.ChaosInsurgency > 0) + ++uniqueFaction; + if (summaryInfo.Anomalies > 0) + ++uniqueFaction; + + return uniqueFaction <= 1; + } + + return false; + } + /// public virtual void AdjustAdditivePipe() { diff --git a/Exiled.CustomModules/API/Features/CustomRoles/SummaryInfo.cs b/Exiled.CustomModules/API/Features/CustomRoles/SummaryInfo.cs new file mode 100644 index 0000000000..6c3b952fd0 --- /dev/null +++ b/Exiled.CustomModules/API/Features/CustomRoles/SummaryInfo.cs @@ -0,0 +1,111 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features.CustomRoles +{ + using System.Collections.Generic; + + using Exiled.API.Enums; + using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Interfaces; + using Exiled.API.Features.Roles; + using Exiled.API.Features.Spawn; + using PlayerRoles; + + /// + /// A tool to easily manage summary info. + /// + public struct SummaryInfo + { + /// + /// The foundation forces. + /// + public int FoundationForces; + + /// + /// The Chaos Insurgency forces. + /// + public int ChaosInsurgency; + + /// + /// The anomalies. + /// + public int Anomalies; + + /// + /// The neutral forces. + /// + public int Neutral; + + /// + /// Gets the current summary. + /// + /// The current summary. + public static SummaryInfo GetSummary() + { + SummaryInfo summary = new(); + + foreach (Player alive in Player.List) + { + if (alive is not Pawn pawn || Round.IgnoredPlayers.Contains(alive)) + continue; + + switch (RoleExtensions.GetTeam(alive.Role.Type)) + { + case Team.Scientists: + case Team.FoundationForces: + ++summary.FoundationForces; + break; + case Team.ClassD: + case Team.ChaosInsurgency: + ++summary.ChaosInsurgency; + break; + case Team.SCPs: + ++summary.Anomalies; + break; + default: + ++summary.Neutral; + break; + } + } + + return summary; + } + + /// + /// Updates the summary. + /// + public void Update() + { + foreach (Player alive in Player.List) + { + if (alive is not Pawn pawn || Round.IgnoredPlayers.Contains(alive)) + continue; + + switch (RoleExtensions.GetTeam(alive.Role.Type)) + { + case Team.Scientists: + case Team.FoundationForces: + ++FoundationForces; + break; + case Team.ClassD: + case Team.ChaosInsurgency: + ++ChaosInsurgency; + break; + case Team.SCPs: + ++Anomalies; + break; + default: + ++Neutral; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/Inventory/IInventorySettings.cs b/Exiled.CustomModules/API/Features/Inventory/IInventorySettings.cs index 6ea61753aa..4d2f1b263c 100644 --- a/Exiled.CustomModules/API/Features/Inventory/IInventorySettings.cs +++ b/Exiled.CustomModules/API/Features/Inventory/IInventorySettings.cs @@ -30,5 +30,10 @@ public interface IInventorySettings /// Gets or sets the ammo box settings to be applied. /// public abstract Dictionary AmmoBox { get; set; } + + /// + /// Gets or sets the custom ammo box settings to be applied. + /// + public abstract Dictionary CustomAmmoBox { get; set; } } } \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/Inventory/InventoryManager.cs b/Exiled.CustomModules/API/Features/Inventory/InventoryManager.cs index fd63c0ad3d..5af8df102a 100644 --- a/Exiled.CustomModules/API/Features/Inventory/InventoryManager.cs +++ b/Exiled.CustomModules/API/Features/Inventory/InventoryManager.cs @@ -29,14 +29,17 @@ public InventoryManager() /// The list of items to be given. /// The list of custom items to be given. /// The ammo box settings to be applied. + /// The custom ammo box settings to be applied. public InventoryManager( List inventory, List customItems, - Dictionary ammoBox) + Dictionary ammoBox, + Dictionary customAmmoBox) { Items = inventory; CustomItems = customItems; AmmoBox = ammoBox; + CustomAmmoBox = customAmmoBox; } /// @@ -48,6 +51,9 @@ public InventoryManager( /// public Dictionary AmmoBox { get; set; } = new(); + /// + public Dictionary CustomAmmoBox { get; set; } = new(); + /// /// Gets or sets the probability associated with this inventory slot. ///
Useful for inventory tweaks involving one or more probability values.
diff --git a/Exiled.CustomModules/API/Features/Pawn.cs b/Exiled.CustomModules/API/Features/Pawn.cs index 05c1bad146..4f1c23517e 100644 --- a/Exiled.CustomModules/API/Features/Pawn.cs +++ b/Exiled.CustomModules/API/Features/Pawn.cs @@ -14,19 +14,20 @@ namespace Exiled.CustomModules.API.Features using Exiled.API.Enums; using Exiled.API.Extensions; using Exiled.API.Features; + using Exiled.API.Features.Attributes; using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Behaviours; using Exiled.API.Features.Items; using Exiled.API.Features.Roles; using Exiled.CustomModules.API.Features.CustomAbilities; using Exiled.CustomModules.API.Features.CustomEscapes; using Exiled.CustomModules.API.Features.CustomItems; + using Exiled.CustomModules.API.Features.CustomItems.Items; using Exiled.CustomModules.API.Features.CustomItems.Pickups.Ammos; using Exiled.CustomModules.API.Features.CustomRoles; using Exiled.CustomModules.API.Features.PlayerAbilities; using Exiled.CustomModules.Events.EventArgs.CustomAbilities; - using PlayerRoles; - using UnityEngine; /// @@ -41,6 +42,7 @@ namespace Exiled.CustomModules.API.Features ///
It serves as a comprehensive representation of an in-game entity, encapsulating the associated with an expanded set of features.
/// ///
+ [DefaultPlayerClass(enforceAuthority: false)] public class Pawn : Player { private readonly List abilityBehaviours = new(); @@ -164,10 +166,63 @@ public Pawn(GameObject gameObject) } } + /// + /// Gets the global role of the pawn. + /// + /// It will return a if available, or the if null. + /// + public object GlobalRole => CustomRole ? CustomRole : Role; + + /// + /// Gets the global items associated with the pawn. + /// + /// It will return a combination of standard s and s. + /// + public IEnumerable GlobalItems => Items.Cast().Concat(CustomItems); + + /// + /// Gets or sets the global current item of the pawn. + /// + /// If a is equipped, it returns the ; otherwise, it returns the regular . + /// + public object GlobalCurrentItem + { + get => CurrentCustomItem ? CurrentCustomItem : CurrentItem; + set + { + if (value is null) + { + Inventory.ServerSelectItem(0); + return; + } + + bool isCustomItem = typeof(CustomItem).IsAssignableFrom(value.GetType()); + if (isCustomItem) + { + if (!CustomItems.Any(customItem => customItem.GetType() == value.GetType())) + { + if (IsInventoryFull) + return; + + AddItem(value); + } + + Item customItem = Items.LastOrDefault(i => i.TryGetComponent(out ItemBehaviour behaviour) && behaviour.GetType() == (value as CustomItem).BehaviourComponent); + Inventory.ServerSelectItem(customItem.Serial); + return; + } + + if (value is not Item item) + return; + + CurrentItem = value as Item; + } + } + /// /// Gets a value indicating whether the pawn is any custom SCP. /// - public bool IsCustomScp => CustomRole is not null && CustomRole.IsScp; + public bool IsCustomScp => CustomRole && CustomRole.IsScp; /// /// Gets a of containing all custom items in the pawn's inventory. @@ -218,7 +273,7 @@ public bool HasCustomItem() /// /// The type of the . /// if a of the specified type was found; otherwise, . - public bool HasCustomAbilty() + public bool HasCustomAbility() where T : PlayerAbility => CustomItems.Any(item => item.GetType() == typeof(T)); /// @@ -249,24 +304,39 @@ public bool TryGetCustomAbility(out T customAbility) where T : PlayerAbility => CustomAbilities.FirstOrDefault(ability => ability.GetType() == typeof(T)).Is(out customAbility); /// - /// Add a of the specified type to the pawn's inventory. + /// Add an or of the specified type to the pawn's inventory. /// - /// The item to be added. + /// The item to be added. /// if the item has been given to the pawn; otherwise, . - public bool AddItem(object customItem) + public bool AddItem(object item) { if (IsInventoryFull) return false; - try - { - uint value = (uint)customItem; - CustomItem.TryGive(this, value); - return true; - } - catch + switch (item) { - return customItem is CustomItem instance && CustomItem.TryGive(this, instance.Id); + case Item baseItem: + AddItem(baseItem); + return true; + case ItemType itemType: + AddItem(itemType); + return true; + case string name: + CustomItem.TryGive(this, name); + return true; + case CustomItem instance when CustomItem.TryGive(this, instance.Id): + return true; + default: + try + { + uint value = (uint)item; + CustomItem.TryGive(this, value); + return true; + } + catch + { + throw new ArgumentException("Item is not of a supported type."); + } } } @@ -299,14 +369,22 @@ public void SetRole(object role, bool preservePlayerPosition = false) if (role is uint id) CustomRole.Spawn(this, id, preservePlayerPosition); - throw new ArgumentException("The type of the role instance is not compatible with RoleTypeId or uint."); + try + { + uint uuId = (uint)role; + CustomRole.Spawn(this, uuId, preservePlayerPosition); + } + catch + { + throw new ArgumentException("The type of the role instance is not compatible with RoleTypeId or uint."); + } } /// /// Safely drops an item. /// /// The item to be dropped. - public void SafeDropItem(Item item) + public new void DropItem(Item item) { if (TryGetCustomItem(out CustomItem customItem)) { @@ -315,7 +393,7 @@ public void SafeDropItem(Item item) return; } - DropItem(item); + base.DropItem(item); } /// @@ -326,7 +404,7 @@ public void SafeDropItem(Item item) public ushort GetAmmo(uint customAmmoType) => (ushort)(customAmmoBox.TryGetValue(customAmmoType, out ushort amount) ? amount : 0); /// - /// Adds an amount of custom ammos to the pawn's ammo box. + /// Adds an amount of custom ammo to the pawn's ammo box. /// /// The type of the custom ammo. /// The amount to be added. @@ -363,7 +441,7 @@ public bool AddAmmo(uint id, ushort amount) } /// - /// Removes an amount of custom ammos from the pawn's ammo box. + /// Removes an amount of custom ammo from the pawn's ammo box. /// /// The type of the custom ammo. /// The amount to be removed. diff --git a/Exiled.CustomModules/API/Features/RespawnManager.cs b/Exiled.CustomModules/API/Features/RespawnManager.cs new file mode 100644 index 0000000000..cdfc8e847a --- /dev/null +++ b/Exiled.CustomModules/API/Features/RespawnManager.cs @@ -0,0 +1,273 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Attributes; + using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Generic.Pools; + using Exiled.API.Features.DynamicEvents; + using Exiled.API.Features.Items; + using Exiled.API.Features.Pickups; + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.CustomRoles; + using Exiled.CustomModules.API.Interfaces; + using Exiled.CustomModules.Events.EventArgs.CustomItems; + using Exiled.CustomModules.Events.EventArgs.CustomRoles; + using Exiled.CustomModules.Events.EventArgs.Tracking; + using Exiled.Events.EventArgs.Map; + using Exiled.Events.EventArgs.Player; + using Exiled.Events.EventArgs.Server; + using HarmonyLib; + using PlayerRoles; + using PlayerRoles.RoleAssign; + using Respawning; + + /// + /// The actor which handles all team respawning tasks for roles. + /// + public class RespawnManager : StaticActor + { + private object nextKnownTeam; + + /// + /// Gets the which handles all delegates to be fired when selecting the next known team. + /// + [DynamicEventDispatcher] + public TDynamicEventDispatcher SelectingCustomTeamRespawnDispatcher { get; private set; } + + /// + /// Gets or sets the next known team. + /// + public object NextKnownTeam + { + get => nextKnownTeam; + set + { + if (value == nextKnownTeam) + return; + + if (value is SpawnableTeamType spawnableTeamType) + { + nextKnownTeam = value; + return; + } + + if (value is uint id && CustomTeam.TryGet(id, out CustomTeam _)) + { + nextKnownTeam = id; + return; + } + + throw new ArgumentException("NextKnownTeam only accepts SpawnableTeamType and parsed uint."); + } + } + + /// + /// Gets or sets the previous spawned team. + /// + public object PreviousKnownTeam { get; protected set; } + + /// + /// Gets all teams' tickets. + /// + public Dictionary> AllTickets { get; } = new(); + + /// + /// Gets all enqueued roles. + /// + public List EnqueuedRoles { get; } + + /// + /// Forces the spawn of a wave of the specified team. + /// + /// The specified team. + public void ForceSpawn(object value) + { + NextKnownTeam = value; + + if (NextKnownTeam is SpawnableTeamType team) + { + Respawning.RespawnManager.Singleton.ForceSpawnTeam(team); + return; + } + + Spawn(); + Respawning.RespawnManager.Singleton.RestartSequence(); + } + + /// + /// Spawns the wave. + /// + public void Spawn() + { + CustomTeam team = CustomTeam.Get((uint)NextKnownTeam); + List toSpawn = Player.Get(Team.Dead).ToList(); + CustomTeam.TrySpawn(toSpawn.Cast(), team); + } + + /// + protected override void PostInitialize_Static() + { + foreach (CustomTeam team in CustomTeam.Get(t => t.UseTickets)) + AllTickets[team.Id] = () => team.Tickets; + } + + /// + protected override void EndPlay_Static() + { + AllTickets.Clear(); + EnqueuedRoles.Clear(); + } + + /// + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + + Exiled.Events.Handlers.Server.RestartedRespawnSequence += OnRestartedRespawnSequence; + Exiled.Events.Handlers.Server.SelectingRespawnTeam += OnSelectingRespawnTeam; + Exiled.Events.Handlers.Server.PreRespawningTeam += OnPreRespawningTeam; + Exiled.Events.Handlers.Server.RespawningTeam += OnRespawningTeam; + Exiled.Events.Handlers.Server.DeployingTeamRole += OnDeployingTeamRole; + Exiled.Events.Handlers.Server.RespawnedTeam += OnRespawnedTeam; + } + + /// + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + + Exiled.Events.Handlers.Server.RestartedRespawnSequence -= OnRestartedRespawnSequence; + Exiled.Events.Handlers.Server.SelectingRespawnTeam -= OnSelectingRespawnTeam; + Exiled.Events.Handlers.Server.PreRespawningTeam -= OnPreRespawningTeam; + Exiled.Events.Handlers.Server.RespawningTeam -= OnRespawningTeam; + Exiled.Events.Handlers.Server.DeployingTeamRole -= OnDeployingTeamRole; + Exiled.Events.Handlers.Server.RespawnedTeam -= OnRespawnedTeam; + } + + private void OnRestartedRespawnSequence(RestartedRespawnSequenceEventArgs ev) + { + if (PreviousKnownTeam is not uint id || !CustomTeam.TryGet(id, out CustomTeam team)) + return; + + ev.TimeForNextSequence = team.NextSequenceTime; + } + + private void OnSelectingRespawnTeam(SelectingRespawnTeamEventArgs ev) + { + NextKnownTeam = null; + foreach (CustomTeam team in CustomTeam.List) + { + if ((team.UseTickets && team.Tickets <= 0) || !team.EvaluateConditions || !team.CanSpawnByProbability) + continue; + + SelectingCustomTeamRespawnEventArgs @event = new((object)team.Id); + SelectingCustomTeamRespawnDispatcher.InvokeAll(@event); + + NextKnownTeam = @event.Team; + return; + } + + if (NextKnownTeam is null) + NextKnownTeam = ev.Team; + } + + private void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) + { + PreviousKnownTeam = NextKnownTeam; + + if (NextKnownTeam is not SpawnableTeamType team) + { + CustomTeam customTeam = CustomTeam.Get((uint)NextKnownTeam); + + if (!customTeam) + return; + + if (customTeam.TeamsOwnership.Any(t => t == (ev.NextKnownTeam is SpawnableTeamType.ChaosInsurgency ? Team.ChaosInsurgency : Team.FoundationForces))) + { + ev.MaxWaveSize = customTeam.Size; + return; + } + + ev.IsAllowed = false; + Spawn(); + Respawning.RespawnManager.Singleton.RestartSequence(); + return; + } + } + + private void OnRespawningTeam(RespawningTeamEventArgs ev) + { + if (NextKnownTeam is not SpawnableTeamType team) + return; + + foreach (CustomRole customRole in CustomRole.List) + { + if (!customRole.IsTeamUnit || customRole.AssignFromTeam != team || !customRole.EvaluateConditions) + continue; + + for (int i = customRole.Instances; i <= customRole.Instances; i++) + { + if (!customRole.CanSpawnByProbability) + continue; + + EnqueuedRoles.Add(customRole.Id); + } + } + + EnqueuedRoles.AddRange(ev.SpawnQueue.Cast()); + + object captain = EnqueuedRoles.FirstOrDefault(role => role is RoleTypeId rId && rId is RoleTypeId.NtfCaptain); + + if (captain is not null) + EnqueuedRoles.Remove(captain); + + EnqueuedRoles.RemoveRange(ev.SpawnQueue.Count - 1, EnqueuedRoles.Count - ev.SpawnQueue.Count); + + if (captain is not null) + { + EnqueuedRoles.Add(captain); + captain = null; + } + } + + private void OnDeployingTeamRole(DeployingTeamRoleEventArgs ev) + { + if (NextKnownTeam is SpawnableTeamType team) + { + object role = EnqueuedRoles.Random(); + + if (role is not uint) + { + EnqueuedRoles.Remove(role); + return; + } + + ev.Delegate = () => + { + CustomRole customRole = CustomRole.Get((uint)role); + ev.Player.Cast().SetRole(customRole); + EnqueuedRoles.Remove(role); + }; + } + + ev.Delegate = () => CustomTeam.TrySpawn(ev.Player.Cast(), (uint)NextKnownTeam); + } + + private void OnRespawnedTeam(RespawnedTeamEventArgs ev) + { + EnqueuedRoles.Clear(); + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/RoleAssigner.cs b/Exiled.CustomModules/API/Features/RoleAssigner.cs new file mode 100644 index 0000000000..bc56dfed5a --- /dev/null +++ b/Exiled.CustomModules/API/Features/RoleAssigner.cs @@ -0,0 +1,339 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Attributes; + using Exiled.API.Features.Core; + using Exiled.API.Features.DynamicEvents; + using Exiled.API.Features.Items; + using Exiled.API.Features.Pickups; + using Exiled.CustomModules.API.Features.CustomRoles; + using Exiled.CustomModules.API.Interfaces; + using Exiled.CustomModules.Events.EventArgs.CustomItems; + using Exiled.CustomModules.Events.EventArgs.CustomRoles; + using Exiled.CustomModules.Events.EventArgs.Tracking; + using Exiled.Events.EventArgs.Map; + using Exiled.Events.EventArgs.Player; + using Exiled.Events.EventArgs.Server; + using HarmonyLib; + using PlayerRoles; + using PlayerRoles.RoleAssign; + + /// + /// The actor which handles all role assignment tasks for roles. + /// + public class RoleAssigner : StaticActor + { + /// + /// Gets the which handles all the delegates fired before assigning human roles. + /// + [DynamicEventDispatcher] + public TDynamicEventDispatcher AssigningHumanCustomRolesDispatcher { get; private set; } + + /// + /// Gets the which handles all the delegates fired before assigning SCP roles. + /// + [DynamicEventDispatcher] + public TDynamicEventDispatcher AssigningScpCustomRolesDispatcher { get; private set; } + + /// + /// Gets or sets all enqueued SCPs. + /// + public List EnqueuedScps { get; protected set; } = new(); + + /// + /// Gets or sets all enqueued humans. + /// + public List EnqueuedHumans { get; protected set; } = new(); + + /// + /// Gets or sets all enqueued players. + /// + public List EnqueuedPlayers { get; protected set; } = new(); + + /// + /// Gets or sets the human roles queue. + /// + public Team[] HumanRolesQueue { get; protected set; } = { }; + + /// + /// Gets a filter to retrieve all available human custom roles. + /// + public Func FilterHumans => customRole => + customRole.AssignFromRole is RoleTypeId.None && + (HumanRolesQueue.Contains(RoleExtensions.GetTeam(customRole.Role)) || customRole.TeamsOwnership.Any(to => HumanRolesQueue.Contains(to))); + + /// + /// Gets a filter to retrieve all available SCP custom roles. + /// + public Func FilterScps => customRole => customRole.IsScp && !customRole.IsTeamUnit && customRole.AssignFromRole is RoleTypeId.None; + + /// + /// Spawns human players based on the provided queue of teams and the length of the queue. + /// + /// An array of teams representing the queue of teams to spawn. + /// The length of the queue. + public virtual void SpawnHumans(Team[] queue, int queueLength) + { + HumanRolesQueue = queue; + EnqueuedHumans.Clear(); + + IEnumerable customRoles = CustomRole.Get(FilterHumans); + List spawnable = GetCustomRolesByProbability(customRoles).ToList(); + + spawnable.ShuffleList(); + + IEnumerable hubs = ReferenceHub.AllHubs.Where(PlayerRoles.RoleAssign.RoleAssigner.CheckPlayer) + .Concat(EnqueuedPlayers.Select(player => player.ReferenceHub)); + + EnqueuedPlayers.Clear(); + + if (spawnable.Count > hubs.Count()) + spawnable.RemoveRange(0, spawnable.Count - hubs.Count()); + + EnqueuedHumans.AddRange(spawnable.Cast()); + + // Unless the custom roles are enough to cover the entire queue, some default roles will be selected. + int num = hubs.Count() - spawnable.Count; + RoleTypeId[] array = num > 0 ? new RoleTypeId[num] : null; + + if (array is not null) + { + for (int i = 0; i < num; i++) + array[i] = HumanSpawner.NextHumanRoleToSpawn; + + array.ShuffleList(); + EnqueuedHumans.AddRange(array.Cast()); + } + + Events.EventArgs.CustomRoles.AssigningHumanCustomRolesEventArgs ev = new(EnqueuedHumans); + AssigningHumanCustomRolesDispatcher.InvokeAll(ev); + EnqueuedHumans = ev.Roles; + + DistributeOrEnqueue(hubs.ToList(), EnqueuedHumans.Where(o => o is uint).Cast(), FilterHumans); + EnqueuedHumans.RemoveAll(o => o is not RoleTypeId); + + for (int j = 0; j < num; j++) + HumanSpawner.AssignHumanRoleToRandomPlayer((RoleTypeId)EnqueuedHumans[j]); + } + + /// RoleTypeId + /// Spawns SCPs based on the target SCP number. + /// + /// The target number of SCPs to spawn. + public virtual void SpawnScps(int targetScpNumber) + { + EnqueuedScps.Clear(); + + IEnumerable customScps = CustomRole.Get(FilterScps); + List spawnable = GetCustomRolesByProbability(CustomRole.Get(FilterScps)).ToList(); + + spawnable.ShuffleList(); + + if (spawnable.Count > targetScpNumber) + spawnable.RemoveRange(0, spawnable.Count - targetScpNumber); + + EnqueuedScps.AddRange(spawnable.Cast()); + + if (spawnable.Count < targetScpNumber) + { + for (int i = 0; i < targetScpNumber - spawnable.Count; i++) + { + RoleTypeId nextScp = ScpSpawner.NextScp; + if (customScps.Any(scp => scp.OverrideScps.Contains(nextScp))) + continue; + + EnqueuedScps.Add((object)nextScp); + } + } + + List chosenPlayers = ScpPlayerPicker.ChoosePlayers(targetScpNumber); + + if (spawnable.Count < targetScpNumber) + EnqueuedPlayers.AddRange(chosenPlayers.Select(rh => Player.Get(rh)).Take(targetScpNumber - spawnable.Count)); + + Events.EventArgs.CustomRoles.AssigningScpCustomRolesEventArgs ev = new(chosenPlayers, EnqueuedScps); + AssigningScpCustomRolesDispatcher.InvokeAll(ev); + EnqueuedScps = ev.Roles; + + List enqueuedScps = EnqueuedScps.Where(role => role is RoleTypeId).Cast().ToList(); + while (enqueuedScps.Count > 0 && chosenPlayers.Count > 0) + { + RoleTypeId scp = enqueuedScps[0]; + enqueuedScps.RemoveAt(0); + ScpSpawner.AssignScp(chosenPlayers, scp, enqueuedScps); + } + + DistributeOrEnqueue(chosenPlayers, EnqueuedScps.Where(o => o is uint).Cast(), FilterScps); + EnqueuedScps.Clear(); + } + + /// + /// Distributes the given roles to all specified players based on a predicate. + /// + /// If a role cannot be assigned due to some circumstances, it will be enqueued until a new role is found. + /// + /// The players to assign the roles to. + /// The roles to be assigned. + /// The predicate. + public void DistributeOrEnqueue(IEnumerable players, List roles, Func predicate) => + DistributeOrEnqueue(players.Select(player => player.ReferenceHub).ToList(), roles, predicate); + + /// + /// Distributes the given roles to all specified players based on a predicate. + /// + /// If a role cannot be assigned due to some circumstances, it will be enqueued until a new role is found. + /// + /// The players to assign the roles to. + /// The roles to be assigned. + /// The predicate. + public virtual void DistributeOrEnqueue(List players, IEnumerable roles, Func predicate) + { + if (roles.IsEmpty()) + return; + + int spawned = 0; + foreach (uint id in roles) + { + if (!CustomRole.TryGet(id, out CustomRole role) || (role.Instances >= role.MaxInstances || !role.EvaluateConditions)) + continue; + + ReferenceHub target = players[0]; + Player.Get(target).Cast().SetRole(role); + players.RemoveAt(0); + ++spawned; + } + + if (spawned < roles.Count()) + { + for (int i = spawned; i == roles.Count(); i++) + { + ReferenceHub target = players[0]; + Player.Get(target).Cast().SetRole(FindAvailableRole(predicate)); + players.RemoveAt(0); + } + } + } + + /// + /// Finds an available based on a predicate. + /// + /// The predicate. + /// The first available . + public CustomRole FindAvailableRole(Func predicate) => + FindAvailableRole(CustomRole.Get(predicate).Shuffle().ToList()); + + /// + /// Finds the first available in the specified list. + /// + /// The list of roles to evaluate. + /// The first available . + public CustomRole FindAvailableRole(List toEvaluate) + { + if (toEvaluate.IsEmpty()) + { + throw new Exception( + "Couldn't find any alternative custom role to assign." + + "Common causes may be circular dependencies into conditions, overridden SCPs or a wrong defined amount of maximum allowed instances."); + } + + CustomRole role = toEvaluate[0]; + + if ((role.IsScp && !role.OverrideScps.IsEmpty()) || role.Instances >= role.MaxInstances || + (role.AssignFromRole is RoleTypeId.None && !role.EvaluateConditions)) + { + toEvaluate.RemoveAt(0); + return FindAvailableRole(toEvaluate); + } + + return role; + } + + /// + /// Gets all custom roles after evaluating their probability. + /// + /// The custom roles to evaluate. + /// All evaluated custom roles. + public virtual IEnumerable GetCustomRolesByProbability(IEnumerable customRoles) + { + foreach (CustomRole customRole in customRoles) + { + for (int i = 0; i == customRole.MaxInstances; i++) + { + if (!customRole.CanSpawnByProbability) + continue; + + yield return customRole.Id; + } + } + } + + /// + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + + Exiled.Events.Handlers.Server.PreAssigningHumanRoles += OnPreAssigningHumanRoles; + Exiled.Events.Handlers.Server.PreAssigningScpRoles += OnPreAssigningScpRoles; + + Exiled.Events.Handlers.Player.ChangingRole += OnChangingRole; + Exiled.Events.Handlers.Player.Spawning += OnSpawning; + } + + /// + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + + Exiled.Events.Handlers.Server.PreAssigningHumanRoles -= OnPreAssigningHumanRoles; + Exiled.Events.Handlers.Server.PreAssigningScpRoles -= OnPreAssigningScpRoles; + + Exiled.Events.Handlers.Player.ChangingRole -= OnChangingRole; + Exiled.Events.Handlers.Player.Spawning -= OnSpawning; + } + + private void OnPreAssigningHumanRoles(PreAssigningHumanRolesEventArgs ev) + { + SpawnHumans(ev.Queue, ev.QueueLength); + ev.IsAllowed = false; + } + + private void OnPreAssigningScpRoles(PreAssigningScpRolesEventArgs ev) + { + SpawnScps(ev.Amount); + ev.IsAllowed = false; + } + + private void OnChangingRole(ChangingRoleEventArgs ev) + { + if (!PlayerRoles.RoleAssign.RoleAssigner.CheckPlayer(ev.Player.ReferenceHub)) + return; + + EnqueuedPlayers.Add(ev.Player.Cast()); + } + + private void OnSpawning(SpawningEventArgs ev) + { + if (!EnqueuedPlayers.Contains(ev.Player) || ev.Player.Cast().HasCustomRole) + return; + + CustomRole customRole = FindAvailableRole(role => role.AssignFromRole == ev.Player.Role); + + if (!customRole) + return; + + customRole.ForceSpawn(ev.Player.Cast(), true); + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/API/Features/TrackerBase.cs b/Exiled.CustomModules/API/Features/TrackerBase.cs index 18e4b9b4b0..2433c94285 100644 --- a/Exiled.CustomModules/API/Features/TrackerBase.cs +++ b/Exiled.CustomModules/API/Features/TrackerBase.cs @@ -30,52 +30,52 @@ public class TrackerBase : StaticActor where T : ITrackable { /// - /// Gets the which handles all the delegates fired when an item is added. + /// Gets or sets the which handles all the delegates fired when an item is added. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher ItemAddedDispatcher { get; private set; } + public TDynamicEventDispatcher ItemAddedDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when an item is removed. + /// Gets or sets the which handles all the delegates fired when an item is removed. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher ItemRemovedDispatcher { get; private set; } + public TDynamicEventDispatcher ItemRemovedDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when an item is restored. + /// Gets or sets the which handles all the delegates fired when an item is restored. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher ItemRestoredDispatcher { get; private set; } + public TDynamicEventDispatcher ItemRestoredDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when an item tracking is modified. + /// Gets or sets the which handles all the delegates fired when an item tracking is modified. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher ItemTrackingModifiedDispatcher { get; private set; } + public TDynamicEventDispatcher ItemTrackingModifiedDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when a pickup is added. + /// Gets or sets the which handles all the delegates fired when a pickup is added. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher PickupAddedDispatcher { get; private set; } + public TDynamicEventDispatcher PickupAddedDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when a pickup is removed. + /// Gets or sets the which handles all the delegates fired when a pickup is removed. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher PickupRemovedDispatcher { get; private set; } + public TDynamicEventDispatcher PickupRemovedDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when a pickup is restored. + /// Gets or sets the which handles all the delegates fired when a pickup is restored. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher PickupRestoredDispatcher { get; private set; } + public TDynamicEventDispatcher PickupRestoredDispatcher { get; set; } /// - /// Gets the which handles all the delegates fired when a pickup tracking is modified. + /// Gets or sets the which handles all the delegates fired when a pickup tracking is modified. /// [DynamicEventDispatcher] - public TDynamicEventDispatcher PickupTrackingModifiedDispatcher { get; private set; } + public TDynamicEventDispatcher PickupTrackingModifiedDispatcher { get; set; } /// /// Gets a containing all serials and their corresponding items. diff --git a/Exiled.CustomModules/API/Features/World.cs b/Exiled.CustomModules/API/Features/World.cs new file mode 100644 index 0000000000..522f399909 --- /dev/null +++ b/Exiled.CustomModules/API/Features/World.cs @@ -0,0 +1,273 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.API.Features +{ + using System.Collections.Generic; + using System.Linq; + + using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Generic; + using Exiled.CustomModules.API.Enums; + using Exiled.CustomModules.API.Features.CustomGameModes; + using Exiled.CustomModules.API.Features.CustomRoles; + using Exiled.CustomModules.Events.EventArgs.CustomRoles; + using Exiled.Events.EventArgs.Server; + using MEC; + + using Server = Exiled.API.Features.Server; + + /// + /// The world base. + /// + public class World : StaticActor + { + /// + /// The default dequeue rate. + /// +#pragma warning disable SA1310 + public const float DEFAULT_DEQUEUE_RATE = 4f; +#pragma warning restore SA1310 + + private GameState gameState; + private CustomGameMode enqueuedGameMode; + private CoroutineHandle queueHandle; + private SummaryInfo summaryInfo; + + /// + /// Gets the . + /// + /// Requires to be enabled. + /// + public GameState GameState => gameState ??= GetComponent(); + + /// + /// Gets or sets the dequeue rate, expressed in seconds. + /// + /// Requires to be enabled. + /// + /// If enqueued, the game mode will be continuously evaluated until it starts or the enqueued game mode changes. + ///
+ /// The defines the delay between each evaluation check. + ///
+ /// + public float DequeueRate { get; set; } = DEFAULT_DEQUEUE_RATE; + + /// + /// Gets the enqueued game mode. + /// + /// Requires to be enabled. + /// + public uint EnqueuedGameMode => enqueuedGameMode.Id; + + /// + /// Gets or sets the running game mode. + /// + /// Requires to be enabled. + /// + public uint RunningGameMode { get; protected internal set; } + + /// + /// Gets the current summary info. + /// + public SummaryInfo SummaryInfo + { + get + { + summaryInfo.Update(); + return summaryInfo; + } + } + + /// + /// Enqueues a custom game mode for execution. + /// + /// Requires to be enabled. + /// + /// The custom game mode to enqueue. + /// Whether to continuously enqueue the game mode. + public virtual void EnqueueGameMode(CustomGameMode customGameMode, bool continuously = false) + { + if (!customGameMode) + return; + + if (queueHandle.IsRunning) + Timing.KillCoroutines(queueHandle); + + enqueuedGameMode = customGameMode; + queueHandle = Timing.RunCoroutine(DequeueGameMode()); + } + + /// + /// Enqueues a custom game mode by its ID for execution. + /// + /// Requires to be enabled. + /// + /// The ID of the custom game mode to enqueue. + /// Whether to continuously enqueue the game mode. + public virtual void EnqueueGameMode(object id, bool continuously = false) + { + if (!CustomGameMode.TryGet(id, out CustomGameMode gameMode)) + return; + + EnqueueGameMode(gameMode, continuously); + } + + /// + /// Clears the current game mode queue. + /// + /// Requires to be enabled. + /// + public virtual void ClearQueue() + { + if (!enqueuedGameMode) + return; + + if (TryGetComponent(enqueuedGameMode.GameState, out EActor comp)) + comp.Destroy(); + + enqueuedGameMode = null; + } + + /// + /// Starts the execution of the enqueued game mode. + /// + /// Requires to be enabled. + /// + /// The custom game mode to start. + /// Whether to force-start the game mode. + public virtual void Start(CustomGameMode customGameMode, bool isForced = false) + { + if (!isForced && customGameMode.Settings.MinimumPlayers < Server.PlayerCount) + return; + + if (GameState) + RemoveComponent(GameState).Destroy(); + + AddComponent(enqueuedGameMode.GameState); + GameState.Start(isForced); + } + + /// + protected override void PostInitialize_Static() + { + if (enqueuedGameMode) + return; + + IEnumerable auto = CustomGameMode.Get(mode => mode.CanStartAuto); + + if (auto.IsEmpty()) + return; + + enqueuedGameMode = auto.Random(); + } + + /// + protected override void Tick() + { + base.Tick(); + + if (GameState.MatchState != UEMatchState.Terminated) + return; + + if (GameState.Settings.RestartRoundOnEnd) + Timing.CallDelayed(GameState.Settings.RestartWindupTime, Server.Restart); + + CanEverTick = false; + } + + /// + protected override void EndPlay_Static() + { + Timing.KillCoroutines(queueHandle); + } + + /// + protected override void SubscribeEvents() + { + base.SubscribeEvents(); + + if (CustomModules.IsModuleLoaded(ModuleType.CustomRoles)) + { + CustomRole.ChangedCustomRoleDispatcher.Bind(this, OnChangedCustomRole); + + Exiled.Events.Handlers.Server.EndingRound += OnEndingRound; + } + + if (CustomModules.IsModuleLoaded(ModuleType.CustomGameModes)) + { + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + } + + /// + protected override void UnsubscribeEvents() + { + base.UnsubscribeEvents(); + + if (CustomModules.IsModuleLoaded(ModuleType.CustomRoles)) + { + CustomRole.ChangedCustomRoleDispatcher.Unbind(this); + + Exiled.Events.Handlers.Server.EndingRound -= OnEndingRound; + } + + if (CustomModules.IsModuleLoaded(ModuleType.CustomGameModes)) + { + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + } + } + + private void OnRoundStarted() + { + EnqueueGameMode(enqueuedGameMode); + } + + private void OnEndingRound(EndingRoundEventArgs ev) + { + if (!ev.IsAllowed) + return; + + foreach (CustomRole role in CustomRole.Get(cr => cr.TeamsOwnership.Length != 1 && cr.Instances > 1)) + { + if (role.Owners.First().RoleBehaviour.EvaluateEndingConditions()) + continue; + + ev.IsAllowed = false; + return; + } + } + + private void OnChangedCustomRole(ChangedCustomRoleEventArgs ev) + { + if (!CustomRole.TryGet(ev.Role, out CustomRole role) || role.TeamsOwnership.Count() == 1) + return; + + Round.IgnoredPlayers.Add(ev.Player); + } + + private IEnumerator DequeueGameMode() + { + for (; ;) + { + if (!enqueuedGameMode) + yield break; + + if (enqueuedGameMode.Settings.MinimumPlayers < Server.PlayerCount) + { + yield return Timing.WaitForSeconds(DequeueRate); + continue; + } + + Start(enqueuedGameMode, true); + ClearQueue(); + } + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/Config.cs b/Exiled.CustomModules/Config.cs index 3262dbd57d..207538ee47 100644 --- a/Exiled.CustomModules/Config.cs +++ b/Exiled.CustomModules/Config.cs @@ -10,6 +10,7 @@ namespace Exiled.CustomModules using System.ComponentModel; using Exiled.API.Interfaces; + using Exiled.CustomModules.API.Enums; /// /// The plugin's config. @@ -26,5 +27,35 @@ public class Config : IConfig /// . [Description("Whether or not debug messages should be shown.")] public bool Debug { get; set; } = false; + + /// + /// Gets or sets a value indicating whether the built-in role assigner should be used over the base game one. + /// + [Description("Whether the built-in role assigner should be used over the base game one.")] + public bool UseDefaultRoleAssigner { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the built-in respawn manager should be used over the base game one. + /// + [Description("Whether the built-in respawn manager should be used over the base game one.")] + public bool UseDefaultRespawnManager { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the automatic modules loader should be used. + /// + /// It iterates over all existing plugins trying to enable all the modules for each plugin's assembly. + ///
+ /// It negatively affects the performance in case of the presence of a big amount of plugins. + ///
+ [Description("Whether the automatic modules loader should be used.")] + public bool UseAutomaticModulesLoader { get; set; } + + /// + /// Gets or sets all modules to be loaded. + /// + [Description("The modules to be loaded.")] + public ModuleType[] Modules { get; set; } = new ModuleType[] + { + }; } } \ No newline at end of file diff --git a/Exiled.CustomModules/CustomModules.cs b/Exiled.CustomModules/CustomModules.cs index fe489a7dd5..ff49af8f4f 100644 --- a/Exiled.CustomModules/CustomModules.cs +++ b/Exiled.CustomModules/CustomModules.cs @@ -7,8 +7,23 @@ namespace Exiled.CustomModules { + using System; + using System.Collections.Generic; + using System.Reflection; + using Exiled.API.Features; + using Exiled.API.Features.Core; + using Exiled.API.Features.Core.Generic; + using Exiled.API.Interfaces; + using Exiled.CustomModules.API.Enums; using Exiled.CustomModules.API.Features; + using Exiled.CustomModules.API.Features.CustomAbilities; + using Exiled.CustomModules.API.Features.CustomEscapes; + using Exiled.CustomModules.API.Features.CustomGameModes; + using Exiled.CustomModules.API.Features.CustomItems; + using Exiled.CustomModules.API.Features.CustomItems.Items; + using Exiled.CustomModules.API.Features.CustomItems.Pickups; + using Exiled.CustomModules.API.Features.CustomRoles; using Exiled.CustomModules.EventHandlers; /// @@ -16,6 +31,16 @@ namespace Exiled.CustomModules /// public class CustomModules : Plugin { + private static readonly Dictionary> ModulesLoader = new() + { + { ModuleType.CustomItems, asm => CustomItem.EnableAll(asm) }, + { ModuleType.CustomRoles, asm => CustomRole.EnableAll(asm) }, + { ModuleType.CustomAbilities, asm => CustomAbility.EnableAll(asm) }, + { ModuleType.CustomTeams, asm => CustomTeam.EnableAll(asm) }, + { ModuleType.CustomEscapes, asm => CustomEscape.EnableAll(asm) }, + { ModuleType.CustomGameModes, asm => CustomGameMode.EnableAll(asm) }, + }; + /// /// Gets a static reference to the plugin's instance. /// @@ -31,12 +56,41 @@ public class CustomModules : Plugin ///
internal ServerHandler ServerHandler { get; private set; } + /// + /// Gets a value indicating whether the specified module is loaded. + /// + /// The module to check. + /// if the module is loaded; otherwise, . + public static bool IsModuleLoaded(ModuleType module) => Instance.Config.Modules.Contains(module); + /// public override void OnEnabled() { Instance = this; - Exiled.Events.Patches.Events.Player.Joined.BasePlayerType = typeof(Pawn); + if (Config.UseAutomaticModulesLoader) + { + foreach (IPlugin plugin in Exiled.Loader.Loader.Plugins) + Config.Modules.ForEach(module => ModulesLoader[module](plugin.Assembly)); + } + + if (Config.Modules.Contains(ModuleType.CustomRoles) && Config.UseDefaultRoleAssigner) + StaticActor.CreateNewInstance(); + + if (Config.Modules.Contains(ModuleType.CustomTeams) && Config.UseDefaultRespawnManager) + StaticActor.CreateNewInstance(); + + if (Config.Modules.Contains(ModuleType.CustomGameModes)) + World.CreateNewInstance(); + + if (Config.Modules.Contains(ModuleType.CustomAbilities)) + StaticActor.CreateNewInstance(); + + if (Config.Modules.Contains(ModuleType.CustomItems)) + { + StaticActor.CreateNewInstance(); + StaticActor.CreateNewInstance(); + } SubscribeEvents(); @@ -46,9 +100,22 @@ public override void OnEnabled() /// public override void OnDisabled() { - UnsubscribeEvents(); + World.Get().Destroy(); - Exiled.Events.Patches.Events.Player.Joined.BasePlayerType = typeof(Player); + StaticActor.Get()?.Destroy(); + StaticActor.Get()?.Destroy(); + StaticActor.Get()?.Destroy(); + StaticActor.Get()?.Destroy(); + StaticActor.Get()?.Destroy(); + + CustomItem.DisableAll(); + CustomRole.DisableAll(); + CustomAbility.DisableAll(); + CustomTeam.DisableAll(); + CustomEscape.DisableAll(); + CustomGameMode.DisableAll(); + + UnsubscribeEvents(); base.OnDisabled(); } diff --git a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddedAbilityEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddedAbilityEventArgs.cs index 2991bcd637..5a155f7b56 100644 --- a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddedAbilityEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddedAbilityEventArgs.cs @@ -12,7 +12,7 @@ namespace Exiled.CustomModules.Events.EventArgs.CustomAbilities using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations after adding an ability. + /// Contains all information after adding an ability. /// /// The type of the . public class AddedAbilityEventArgs diff --git a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddingAbilityEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddingAbilityEventArgs.cs index 3e97a035ee..4b2b5b19e9 100644 --- a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddingAbilityEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/AddingAbilityEventArgs.cs @@ -12,7 +12,7 @@ namespace Exiled.CustomModules.Events.EventArgs.CustomAbilities using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations before adding an ability. + /// Contains all information before adding an ability. /// /// The type of the . public class AddingAbilityEventArgs : IDeniableEvent diff --git a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovedAbilityEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovedAbilityEventArgs.cs index 02335306e0..743d9b43b5 100644 --- a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovedAbilityEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovedAbilityEventArgs.cs @@ -12,7 +12,7 @@ namespace Exiled.CustomModules.Events.EventArgs.CustomAbilities using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations after removing an ability. + /// Contains all information after removing an ability. /// /// The type of the . public class RemovedAbilityEventArgs diff --git a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovingAbilityEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovingAbilityEventArgs.cs index d057fca5cb..48dddb5594 100644 --- a/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovingAbilityEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/CustomAbilities/RemovingAbilityEventArgs.cs @@ -12,7 +12,7 @@ namespace Exiled.CustomModules.Events.EventArgs.CustomAbilities using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations before removing an ability. + /// Contains all information before removing an ability. /// /// The type of the . public class RemovingAbilityEventArgs : IDeniableEvent diff --git a/Exiled.CustomModules/Events/EventArgs/CustomEscapes/EscapingEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomEscapes/EscapingEventArgs.cs index 8733e45adc..8e83207e8e 100644 --- a/Exiled.CustomModules/Events/EventArgs/CustomEscapes/EscapingEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/CustomEscapes/EscapingEventArgs.cs @@ -14,7 +14,7 @@ namespace Exiled.CustomModules.Events.EventArgs.CustomEscapes using PlayerRoles; /// - /// Contains all informations before escaping. + /// Contains all information before escaping. /// public class EscapingEventArgs : IPlayerEvent, IDeniableEvent { diff --git a/Exiled.CustomModules/Events/EventArgs/CustomRoles/AssigningHumanCustomRolesEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomRoles/AssigningHumanCustomRolesEventArgs.cs new file mode 100644 index 0000000000..ac5b2ee84d --- /dev/null +++ b/Exiled.CustomModules/Events/EventArgs/CustomRoles/AssigningHumanCustomRolesEventArgs.cs @@ -0,0 +1,32 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.Events.EventArgs.CustomRoles +{ + using System.Collections.Generic; + + using API.Enums; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles; + + /// + /// Contains all information before assigning human roles. + /// + public class AssigningHumanCustomRolesEventArgs : IExiledEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + public AssigningHumanCustomRolesEventArgs(List roles) => Roles = roles; + + /// + /// Gets or sets all roles to be assigned. + /// + public List Roles { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/Events/EventArgs/CustomRoles/AssigningScpCustomRolesEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomRoles/AssigningScpCustomRolesEventArgs.cs new file mode 100644 index 0000000000..6f1ed74caa --- /dev/null +++ b/Exiled.CustomModules/Events/EventArgs/CustomRoles/AssigningScpCustomRolesEventArgs.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.Events.EventArgs.CustomRoles +{ + using System.Collections.Generic; + using System.Linq; + + using API.Enums; + using API.Features; + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles; + + /// + /// Contains all information before assigning SCP roles. + /// + public class AssigningScpCustomRolesEventArgs : IExiledEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public AssigningScpCustomRolesEventArgs(List chosenPlayers, List enqueuedScps) + { + Players = Player.Get(chosenPlayers).ToList(); + Roles = enqueuedScps; + } + + /// + /// Gets or sets the players to be spawned. + /// + public List Players { get; set; } + + /// + /// Gets or sets all roles to be assigned. + /// + public List Roles { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/Events/EventArgs/CustomRoles/ChangedCustomRoleEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomRoles/ChangedCustomRoleEventArgs.cs new file mode 100644 index 0000000000..3a3e20ac66 --- /dev/null +++ b/Exiled.CustomModules/Events/EventArgs/CustomRoles/ChangedCustomRoleEventArgs.cs @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.Events.EventArgs.CustomRoles +{ + using System.Collections.Generic; + + using API.Enums; + using Exiled.API.Features; + using Exiled.CustomModules.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles; + + /// + /// Contains all information after a player changes role to a custom role. + /// + public class ChangedCustomRoleEventArgs : IExiledEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ChangedCustomRoleEventArgs(Player player, object role) + { + Player = player; + Role = role; + } + + /// + /// Gets the player who changed role. + /// + public Player Player { get; } + + /// + /// Gets the previous role. + /// + public object Role { get; } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/Events/EventArgs/CustomRoles/ChangingCustomRoleEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomRoles/ChangingCustomRoleEventArgs.cs new file mode 100644 index 0000000000..edcfe38ff3 --- /dev/null +++ b/Exiled.CustomModules/Events/EventArgs/CustomRoles/ChangingCustomRoleEventArgs.cs @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.Events.EventArgs.CustomRoles +{ + using System.Collections.Generic; + + using API.Enums; + using Exiled.API.Features; + using Exiled.CustomModules.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles; + + /// + /// Contains all information before a player changes role to a custom role. + /// + public class ChangingCustomRoleEventArgs : IExiledEvent, IPlayerEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public ChangingCustomRoleEventArgs(Player player, object role, bool isAllowed = true) + { + Player = player; + Role = role; + IsAllowed = isAllowed; + } + + /// + /// Gets or sets the player who's changing role. + /// + public Player Player { get; set; } + + /// + /// Gets or sets the role to be changed to. + /// + /// Supports both roles and custom roles. + /// + public object Role { get; set; } + + /// + /// Gets or sets a value indicating whether the player can change role. + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/Events/EventArgs/CustomRoles/SelectingCustomTeamRespawnEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/CustomRoles/SelectingCustomTeamRespawnEventArgs.cs new file mode 100644 index 0000000000..613976ebc3 --- /dev/null +++ b/Exiled.CustomModules/Events/EventArgs/CustomRoles/SelectingCustomTeamRespawnEventArgs.cs @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.CustomModules.Events.EventArgs.CustomRoles +{ + using System.Collections.Generic; + using System.Linq; + + using API.Enums; + using API.Features; + using Exiled.API.Features; + using Exiled.CustomModules.API.Features.CustomRoles; + using Exiled.Events.EventArgs.Interfaces; + using PlayerRoles; + using Respawning; + + /// + /// Contains all information before selecting the next known team. + /// + public class SelectingCustomTeamRespawnEventArgs : IExiledEvent + { + private object team; + + /// + /// Initializes a new instance of the class. + /// + /// + public SelectingCustomTeamRespawnEventArgs(object team) + { + this.team = team; + } + + /// + /// Gets or sets the next known team. + /// + public object Team + { + get => team; + set + { + if (value == team) + return; + + if (value is SpawnableTeamType teamType) + { + team = (object)teamType; + return; + } + + if (CustomTeam.TryGet(value, out CustomTeam customTeam)) + { + team = (object)customTeam.Id; + return; + } + } + } + } +} \ No newline at end of file diff --git a/Exiled.CustomModules/Events/EventArgs/Tracking/ItemTrackingModifiedEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/Tracking/ItemTrackingModifiedEventArgs.cs index e1d7a1026a..81e38ad053 100644 --- a/Exiled.CustomModules/Events/EventArgs/Tracking/ItemTrackingModifiedEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/Tracking/ItemTrackingModifiedEventArgs.cs @@ -17,7 +17,7 @@ namespace Exiled.CustomModules.Events.EventArgs.Tracking using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations after modifying an item tracking. + /// Contains all information after modifying an item tracking. /// public class ItemTrackingModifiedEventArgs : TrackingModifiedEventArgs, IItemEvent { diff --git a/Exiled.CustomModules/Events/EventArgs/Tracking/PickupTrackingModifiedEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/Tracking/PickupTrackingModifiedEventArgs.cs index e7922df2eb..1dbbcc2df3 100644 --- a/Exiled.CustomModules/Events/EventArgs/Tracking/PickupTrackingModifiedEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/Tracking/PickupTrackingModifiedEventArgs.cs @@ -15,7 +15,7 @@ namespace Exiled.CustomModules.Events.EventArgs.Tracking using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations after modifying a pickup tracking. + /// Contains all information after modifying a pickup tracking. /// public class PickupTrackingModifiedEventArgs : TrackingModifiedEventArgs, IPickupEvent { diff --git a/Exiled.CustomModules/Events/EventArgs/Tracking/TrackingModifiedEventArgs.cs b/Exiled.CustomModules/Events/EventArgs/Tracking/TrackingModifiedEventArgs.cs index c76a029cf7..0631f83061 100644 --- a/Exiled.CustomModules/Events/EventArgs/Tracking/TrackingModifiedEventArgs.cs +++ b/Exiled.CustomModules/Events/EventArgs/Tracking/TrackingModifiedEventArgs.cs @@ -14,7 +14,7 @@ namespace Exiled.CustomModules.Events.EventArgs.Tracking using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all informations after modifying a tracking. + /// Contains all information after modifying a tracking. /// public class TrackingModifiedEventArgs : IExiledEvent { diff --git a/Exiled.CustomModules/Patches/CustomItems/PlayerInventorySee.cs b/Exiled.CustomModules/Patches/PlayerInventorySee.cs similarity index 97% rename from Exiled.CustomModules/Patches/CustomItems/PlayerInventorySee.cs rename to Exiled.CustomModules/Patches/PlayerInventorySee.cs index cbea214e09..bf9fa03cc7 100644 --- a/Exiled.CustomModules/Patches/CustomItems/PlayerInventorySee.cs +++ b/Exiled.CustomModules/Patches/PlayerInventorySee.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Exiled.CustomModules.Patches.CustomItems +namespace Exiled.CustomModules.Patches { using System.Collections.Generic; using System.Reflection; @@ -13,11 +13,11 @@ namespace Exiled.CustomModules.Patches.CustomItems using CommandSystem.Commands.RemoteAdmin; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Items; - using Exiled.API.Features.Pools; using Exiled.CustomModules.API.Features.CustomItems; - using HarmonyLib; + using HarmonyLib; using InventorySystem.Items; using static HarmonyLib.AccessTools; diff --git a/Exiled.Events/Commands/PluginManager/Show.cs b/Exiled.Events/Commands/PluginManager/Show.cs index b703d44fcd..02b8a9e1d8 100644 --- a/Exiled.Events/Commands/PluginManager/Show.cs +++ b/Exiled.Events/Commands/PluginManager/Show.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Commands.PluginManager using System.Linq; using System.Text; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using API.Interfaces; using CommandSystem; diff --git a/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs b/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs index 2636eb26fd..9029d442a5 100644 --- a/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs +++ b/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs @@ -38,7 +38,7 @@ public AnnouncingScpTerminationEventArgs(Player scp, DamageHandlerBase damageHan Player = scp; Role = scp.Role; DamageHandler = new CustomDamageHandler(scp, damageHandlerBase); - Attacker = DamageHandler.BaseIs(out CustomAttackerHandler customAttackerHandler) ? customAttackerHandler.Attacker : null; + Attacker = DamageHandler.Is(out CustomAttackerHandler customAttackerHandler) ? customAttackerHandler.Attacker : null; TerminationCause = damageHandlerBase.CassieDeathAnnouncement.Announcement; IsAllowed = isAllowed; } diff --git a/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs b/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs index 7fadaccfbe..6a003096d7 100644 --- a/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs +++ b/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs @@ -10,9 +10,9 @@ namespace Exiled.Events.EventArgs.Map using System.Collections.Generic; using Exiled.API.Features; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.API.Features.Pickups; using Exiled.API.Features.Pickups.Projectiles; - using Exiled.API.Features.Pools; using Exiled.Events.EventArgs.Interfaces; using Exiled.Events.Patches.Generic; @@ -67,7 +67,7 @@ public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionG break; case true: { - if (Server.FriendlyFire || thrower.Hub == Server.Host.ReferenceHub || HitboxIdentity.CheckFriendlyFire(thrower.Role, hub.roleManager.CurrentRole.RoleTypeId)) + if (Server.FriendlyFire || thrower.Hub == Server.Host.ReferenceHub || HitboxIdentity.IsEnemy(thrower.Role, hub.roleManager.CurrentRole.RoleTypeId)) { if (!TargetsToAffect.Contains(player)) TargetsToAffect.Add(player); diff --git a/Exiled.Events/EventArgs/Map/PlacingPickupIntoPocketDimensionEventArgs.cs b/Exiled.Events/EventArgs/Map/PlacingPickupIntoPocketDimensionEventArgs.cs new file mode 100644 index 0000000000..f975da50a8 --- /dev/null +++ b/Exiled.Events/EventArgs/Map/PlacingPickupIntoPocketDimensionEventArgs.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Map +{ + using Exiled.API.Features.Pickups; + using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items.Pickups; + + using UnityEngine; + + using static PlayerRoles.PlayableScps.Scp106.Scp106PocketItemManager; + + /// + /// Contains information about items in the pocket dimension. + /// + public class PlacingPickupIntoPocketDimensionEventArgs : IDeniableEvent, IPickupEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public PlacingPickupIntoPocketDimensionEventArgs(ItemPickupBase pickupBase, PocketItem pocketItem, bool isAllowed) + { + Pickup = Pickup.Get(pickupBase); + PocketItem = pocketItem; + IsAllowed = isAllowed; + } + + /// + public Pickup Pickup { get; } + + /// + /// Gets the value of the PocketItem. + /// + public PocketItem PocketItem { get; } + + /// + /// Gets or sets a value indicating when the Pickup will be dropped onto the map. + /// + public double DropTime + { + get => PocketItem.TriggerTime; + set => PocketItem.TriggerTime = value; + } + + /// + /// Gets or sets a value indicating whether the pickup should be removed from the Pocket Dimension. + /// + public bool ShouldRemove + { + get => PocketItem.Remove; + set => PocketItem.Remove = value; + } + + /// + /// Gets or sets a value indicating whether the warning sound for the pickup should be sent. + /// + public bool ShouldWarn + { + get => !PocketItem.WarningSent; + set => PocketItem.WarningSent = !value; + } + + /// + /// Gets or sets the location where the pickup will drop onto the map. + /// + public Vector3 Position + { + get => PocketItem.DropPosition.Position; + set => PocketItem.DropPosition = new(value); + } + + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs b/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs index 552d70a73b..278fee05a2 100644 --- a/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.EventArgs.Player using API.Enums; using API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Interfaces; using InventorySystem.Configs; diff --git a/Exiled.Events/EventArgs/Player/DiedEventArgs.cs b/Exiled.Events/EventArgs/Player/DiedEventArgs.cs index c6606c9b7a..32f1c49093 100644 --- a/Exiled.Events/EventArgs/Player/DiedEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/DiedEventArgs.cs @@ -35,7 +35,7 @@ public class DiedEventArgs : IPlayerEvent, IAttackerEvent public DiedEventArgs(Player target, RoleTypeId targetOldRole, DamageHandlerBase damageHandler) { DamageHandler = new CustomDamageHandler(target, damageHandler); - Attacker = DamageHandler.BaseIs(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; + Attacker = DamageHandler.Is(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; Player = target; TargetOldRole = targetOldRole; } diff --git a/Exiled.Events/EventArgs/Player/DisplayingHitmarkerEventArgs.cs b/Exiled.Events/EventArgs/Player/DisplayingHitmarkerEventArgs.cs new file mode 100644 index 0000000000..f210101890 --- /dev/null +++ b/Exiled.Events/EventArgs/Player/DisplayingHitmarkerEventArgs.cs @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all information before displaying the hitmarker to the player. + /// + public class DisplayingHitmarkerEventArgs : IDeniableEvent, IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public DisplayingHitmarkerEventArgs(Player player, float size, bool playAudio, bool isAllowed = true) + { + Player = player; + Size = size; + PlayAudio = playAudio; + IsAllowed = isAllowed; + } + + /// + public bool IsAllowed { get; set; } + + /// + public Player Player { get; } + + /// + /// Gets or sets a value indicating whether audio should be played. + /// + public bool PlayAudio { get; set; } + + /// + /// Gets or sets the hitmarker's size. + /// + public float Size { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Player/DyingEventArgs.cs b/Exiled.Events/EventArgs/Player/DyingEventArgs.cs index 1a96627e78..fae5d10ffe 100644 --- a/Exiled.Events/EventArgs/Player/DyingEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/DyingEventArgs.cs @@ -38,21 +38,14 @@ public DyingEventArgs(Player target, DamageHandlerBase damageHandler) { DamageHandler = new CustomDamageHandler(target, damageHandler); Player = target; -#pragma warning disable CS0618 ItemsToDrop = Player.Items.ToList(); -#pragma warning restore CS0618 - Attacker = DamageHandler.BaseIs(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; + Attacker = DamageHandler.Is(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; } /// - /// Gets or sets the list of items to be dropped. + /// Gets the list of items to be dropped. /// - public List ItemsToDrop - { - get; - [Obsolete("This setter has been deprecated")] - set; - } + public List ItemsToDrop { get; } /// /// Gets the dying player. diff --git a/Exiled.Events/EventArgs/Player/HurtEventArgs.cs b/Exiled.Events/EventArgs/Player/HurtEventArgs.cs index 80cff1c51b..24158d95cf 100644 --- a/Exiled.Events/EventArgs/Player/HurtEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/HurtEventArgs.cs @@ -35,7 +35,7 @@ public class HurtEventArgs : IAttackerEvent public HurtEventArgs(Player target, DamageHandlerBase damageHandler, DamageHandlerBase.HandlerOutput handlerOutput) { DamageHandler = new CustomDamageHandler(target, damageHandler); - Attacker = DamageHandler.BaseIs(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; + Attacker = DamageHandler.Is(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; Player = target; HandlerOutput = handlerOutput; } diff --git a/Exiled.Events/EventArgs/Player/HurtingEventArgs.cs b/Exiled.Events/EventArgs/Player/HurtingEventArgs.cs index b5b8912ac3..794c3eadcf 100644 --- a/Exiled.Events/EventArgs/Player/HurtingEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/HurtingEventArgs.cs @@ -33,7 +33,7 @@ public HurtingEventArgs(Player target, DamageHandlerBase damageHandler) { DamageHandler = new CustomDamageHandler(target, damageHandler); - Attacker = DamageHandler.BaseIs(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; + Attacker = DamageHandler.Is(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; Player = target; } diff --git a/Exiled.Events/EventArgs/Player/LeftEventArgs.cs b/Exiled.Events/EventArgs/Player/LeftEventArgs.cs index 05fde1f1c2..b4997c2123 100644 --- a/Exiled.Events/EventArgs/Player/LeftEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/LeftEventArgs.cs @@ -8,19 +8,22 @@ namespace Exiled.Events.EventArgs.Player { using API.Features; + using Exiled.Events.EventArgs.Interfaces; /// - /// Contains all information after a disconnects from the server. + /// Contains all information after a disconnects from the server. /// - public class LeftEventArgs : JoinedEventArgs + public class LeftEventArgs : IPlayerEvent { /// /// Initializes a new instance of the class. /// /// The player who left the server. - public LeftEventArgs(Player player) - : base(player) - { - } + public LeftEventArgs(Player player) => Player = player; + + /// + /// Gets the left player. + /// + public Player Player { get; } } } \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs b/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs index 6d2366364d..58e9f47628 100644 --- a/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs @@ -44,7 +44,6 @@ public SearchingPickupEventArgs(Player player, ItemPickupBase pickup, SearchSess SearchSession = searchSession; SearchCompletor = searchCompletor; SearchTime = searchTime; - IsAllowed = searchCompletor.ValidateStart(); } /// @@ -65,7 +64,7 @@ public SearchingPickupEventArgs(Player player, ItemPickupBase pickup, SearchSess /// /// Gets or sets a value indicating whether the Pickup can be searched. /// - public bool IsAllowed { get; set; } + public bool IsAllowed { get; set; } = true; /// /// Gets the Pickup that is being searched. diff --git a/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs b/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs index 13e7f25bcf..b486a892f5 100644 --- a/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs +++ b/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs @@ -28,16 +28,12 @@ public class SpawningRagdollEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - /// - /// - /// /// /// /// - public SpawningRagdollEventArgs(RagdollData info, DamageHandlerBase damageHandlerBase, bool isAllowed = true) + public SpawningRagdollEventArgs(RagdollData info, bool isAllowed = true) { Info = info; - DamageHandlerBase = damageHandlerBase; Player = Player.Get(info.OwnerHub); Scale = Player.Scale; IsAllowed = isAllowed; @@ -97,7 +93,11 @@ public string Nickname /// /// Gets or sets the ragdoll's . /// - public DamageHandlerBase DamageHandlerBase { get; set; } + public DamageHandlerBase DamageHandlerBase + { + get => Info.Handler; + set => Info = new RagdollData(Player.ReferenceHub, value, Role, Position, Rotation, Nickname, CreationTime); + } /// /// Gets or sets a value indicating whether or not the ragdoll can be spawned. diff --git a/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs b/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs index e897dbb04a..e2c2c0728c 100644 --- a/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs +++ b/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; + IsAllowed = isAllowed && Target.Role is SpectatorRole spectatorRole && spectatorRole.IsReadyToRespawn; } /// diff --git a/Exiled.Events/EventArgs/Scp173/AddingObserverEventArgs.cs b/Exiled.Events/EventArgs/Scp173/AddingObserverEventArgs.cs new file mode 100644 index 0000000000..c33a327bf3 --- /dev/null +++ b/Exiled.Events/EventArgs/Scp173/AddingObserverEventArgs.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.Scp173 +{ + using API.Features; + using Exiled.API.Features.Roles; + using Interfaces; + + /// + /// Contains all information before a player sees SCP-173. + /// + public class AddingObserverEventArgs : IScp173Event, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public AddingObserverEventArgs(Player player, Player target, bool isAllowed = true) + { + Scp173 = player.Role.As(); + Player = player; + Target = target; + IsAllowed = isAllowed; + } + + /// + public Scp173Role Scp173 { get; } + + /// + /// Gets the target who has looked at SCP-173. + /// + public Player Target { get; } + + /// + /// Gets the player who's controlling SCP-173. + /// + public Player Player { get; } + + /// + /// Gets or sets a value indicating whether the player can be added as an observer. + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Scp173/RemoveObserverEventArgs.cs b/Exiled.Events/EventArgs/Scp173/RemoveObserverEventArgs.cs new file mode 100644 index 0000000000..a044f50f64 --- /dev/null +++ b/Exiled.Events/EventArgs/Scp173/RemoveObserverEventArgs.cs @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp173 +{ + using API.Features; + using Exiled.API.Features.Roles; + using Interfaces; + + /// + /// Contains all information after a player stops looking at SCP-173. + /// + public class RemoveObserverEventArgs : IScp173Event + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public RemoveObserverEventArgs(Player player, Player target) + { + Scp173 = player.Role.As(); + Player = player; + Target = target; + } + + /// + public Scp173Role Scp173 { get; } + + /// + /// Gets the player who's controlling SCP-173. + /// + public Player Player { get; } + + /// + /// Gets the target who no longer sees SCP-173. + /// + public Player Target { get; } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs b/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs index d24a57ad40..bd56a64369 100644 --- a/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs +++ b/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs @@ -32,9 +32,9 @@ public EatingScp330EventArgs(Player player, ICandy candy, bool isAllowed = true) } /// - /// Gets the that is being eaten by the player. + /// Gets or sets the that is being eaten by the player. /// - public ICandy Candy { get; } + public ICandy Candy { get; set; } /// /// Gets or sets a value indicating whether or not the player can eat SCP-330. diff --git a/Exiled.Events/EventArgs/Server/AssigningHumanRolesEventArgs.cs b/Exiled.Events/EventArgs/Server/AssigningHumanRolesEventArgs.cs new file mode 100644 index 0000000000..dc61481a5f --- /dev/null +++ b/Exiled.Events/EventArgs/Server/AssigningHumanRolesEventArgs.cs @@ -0,0 +1,32 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using API.Enums; + + using Interfaces; + + using PlayerRoles; + + /// + /// Contains all information before assigning human roles. + /// + public class AssigningHumanRolesEventArgs : IExiledEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + public AssigningHumanRolesEventArgs(RoleTypeId[] roles) => Roles = roles; + + /// + /// Gets or sets all roles to be assigned. + /// + public RoleTypeId[] Roles { get; set; } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/AssigningScpRolesEventArgs.cs b/Exiled.Events/EventArgs/Server/AssigningScpRolesEventArgs.cs new file mode 100644 index 0000000000..7d9d6c0a06 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/AssigningScpRolesEventArgs.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.Server +{ + using System.Collections.Generic; + using System.Linq; + + using API.Enums; + using API.Features; + + using Interfaces; + + using PlayerRoles; + + /// + /// Contains all information before assigning SCP roles. + /// + public class AssigningScpRolesEventArgs : IExiledEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public AssigningScpRolesEventArgs(List chosenPlayers, List enqueuedScps) + { + Players = Player.Get(chosenPlayers).ToList(); + Roles = enqueuedScps; + } + + /// + /// Gets or sets the players to be spawned. + /// + public List Players { get; set; } + + /// + /// Gets or sets all roles to be assigned. + /// + public List Roles { get; set; } + + /// + /// Gets all chosen player's reference hubs. + /// + internal List ChosenPlayers => Players.Select(player => player.ReferenceHub).ToList(); + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/ChoosingStartTeamQueueEventArgs.cs b/Exiled.Events/EventArgs/Server/ChoosingStartTeamQueueEventArgs.cs index 47d0180c8e..f216315bc1 100644 --- a/Exiled.Events/EventArgs/Server/ChoosingStartTeamQueueEventArgs.cs +++ b/Exiled.Events/EventArgs/Server/ChoosingStartTeamQueueEventArgs.cs @@ -11,21 +11,19 @@ namespace Exiled.Events.EventArgs.Server using System.Collections.Generic; using System.Text; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Interfaces; using PlayerRoles; /// - /// Contains all information before a spectator changes the spectated player. + /// Contains all information choosing the team to be assigned to a player. /// public class ChoosingStartTeamQueueEventArgs : IDeniableEvent { /// /// Initializes a new instance of the class. /// - /// - /// - /// + /// public ChoosingStartTeamQueueEventArgs(string teamRespawnQueue) { TeamRespawnQueue = new(); @@ -38,19 +36,19 @@ public ChoosingStartTeamQueueEventArgs(string teamRespawnQueue) } /// - /// Gets the TeamRespawnQueue. + /// Gets the team respawn queue. /// public List TeamRespawnQueue { get; } /// - /// Gets or sets a value indicating whether the event can continue. + /// Gets or sets a value indicating whether the team can be assigned. /// public bool IsAllowed { get; set; } = true; /// - /// Gets the TeamRespawnQueue in a string value. + /// Gets the team respawn queue in a string value. /// - /// The actual modified TeamRespawnQueue. + /// The actual modified team respawn queue. internal string GetTeamRespawnQueue() { StringBuilder teamRespawnQueue = StringBuilderPool.Pool.Get(); diff --git a/Exiled.Events/EventArgs/Server/DeployingHumanRoleEventArgs.cs b/Exiled.Events/EventArgs/Server/DeployingHumanRoleEventArgs.cs new file mode 100644 index 0000000000..f2f12ece53 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/DeployingHumanRoleEventArgs.cs @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using API.Enums; + using API.Features; + + using Interfaces; + + using PlayerRoles; + using PlayerRoles.RoleAssign; + + /// + /// Contains all information before deploying a human role. + /// + public class DeployingHumanRoleEventArgs : IExiledEvent + { + private RoleTypeId role; + private bool delegateHasChanged; + private Action @delegate; + + /// + /// Initializes a new instance of the class. + /// + /// + public DeployingHumanRoleEventArgs(RoleTypeId selectedRole) + { + Role = selectedRole; + @delegate = () => HumanSpawner.AssignHumanRoleToRandomPlayer(selectedRole); + OriginalDelegate = @delegate; + } + + /// + /// Gets or sets the to be fired to deploy the role. + /// + public Action Delegate + { + get => @delegate; + set + { + @delegate = value; + delegateHasChanged = true; + } + } + + /// + /// Gets the original to be fired to deploy the role. + /// + public Action OriginalDelegate { get; } + + /// + /// Gets or sets the role to be deployed. + /// + public RoleTypeId Role + { + get => role; + set + { + if (role == value) + return; + + role = value; + + if (delegateHasChanged) + return; + + @delegate = () => HumanSpawner.AssignHumanRoleToRandomPlayer(role); + } + } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/DeployingScpRoleEventArgs.cs b/Exiled.Events/EventArgs/Server/DeployingScpRoleEventArgs.cs new file mode 100644 index 0000000000..0976e53532 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/DeployingScpRoleEventArgs.cs @@ -0,0 +1,94 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using API.Enums; + using API.Features; + + using Interfaces; + + using PlayerRoles; + using PlayerRoles.RoleAssign; + + /// + /// Contains all information before deploying a SCP role. + /// + public class DeployingScpRoleEventArgs : IExiledEvent + { + private RoleTypeId role; + private bool delegateHasChanged; + private Action @delegate; + + /// + /// Initializes a new instance of the class. + /// + /// The players from which retrieve the player to be spawned from. + /// + /// All enqueued SCPs. + public DeployingScpRoleEventArgs(List chosenPlayers, RoleTypeId selectedRole, List enqueuedScps) + { + ChosenPlayers = chosenPlayers; + Roles = enqueuedScps; + Role = role; + @delegate = () => ScpSpawner.AssignScp(chosenPlayers, selectedRole, enqueuedScps); + OriginalDelegate = @delegate; + } + + /// + /// Gets or sets the to be fired to deploy the role. + /// + public Action Delegate + { + get => @delegate; + set + { + @delegate = value; + delegateHasChanged = true; + } + } + + /// + /// Gets the original to be fired to deploy the role. + /// + public Action OriginalDelegate { get; } + + /// + /// Gets or sets the role to be deployed. + /// + public RoleTypeId Role + { + get => role; + set + { + if (role == value) + return; + + role = value; + + if (delegateHasChanged) + return; + + @delegate = () => ScpSpawner.AssignScp(ChosenPlayers, role, Roles); + } + } + + /// + /// Gets all roles to be assigned. + /// + public List Roles { get; } + + /// + /// Gets all chosen player's reference hubs. + /// + internal List ChosenPlayers { get; } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/DeployingTeamRoleEventArgs.cs b/Exiled.Events/EventArgs/Server/DeployingTeamRoleEventArgs.cs new file mode 100644 index 0000000000..2de36df982 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/DeployingTeamRoleEventArgs.cs @@ -0,0 +1,110 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using API.Enums; + using API.Features; + + using Interfaces; + + using PlayerRoles; + using PlayerRoles.RoleAssign; + + /// + /// Contains all information before deploying a team role. + /// + public class DeployingTeamRoleEventArgs : IExiledEvent, IPlayerEvent + { + private RoleTypeId role; + private bool delegateHasChanged; + private Action @delegate; + private Player player; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public DeployingTeamRoleEventArgs(ReferenceHub hub, RoleTypeId selectedRole) + { + Player = Player.Get(hub); + Role = selectedRole; + @delegate = () => hub.roleManager.ServerSetRole(selectedRole, RoleChangeReason.Respawn); + OriginalDelegate = @delegate; + } + + /// + /// Gets or sets the to be fired to deploy the role. + /// + public Action Delegate + { + get => @delegate; + set + { + @delegate = value; + delegateHasChanged = true; + } + } + + /// + /// Gets the original to be fired to deploy the role. + /// + public Action OriginalDelegate { get; } + + /// + /// Gets or sets the player to be deployed. + /// + public Player Player + { + get => player; + set + { + if (player == value) + return; + + player = value; + + if (delegateHasChanged) + return; + + @delegate = () => Player.ReferenceHub.roleManager.ServerSetRole(role, RoleChangeReason.Respawn); + } + } + + /// + /// Gets or sets a value indicating whether the role's deployment is reliable. + /// + /// When set to , the will not be counted among respawned players. + /// + public bool IsReliable { get; set; } = true; + + /// + /// Gets or sets the role to be deployed. + /// + public RoleTypeId Role + { + get => role; + set + { + if (role == value) + return; + + role = value; + + if (delegateHasChanged) + return; + + @delegate = () => Player.ReferenceHub.roleManager.ServerSetRole(role, RoleChangeReason.Respawn); + } + } + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs b/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs index 03e4a538e4..e0937dd8ae 100644 --- a/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs +++ b/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs @@ -18,23 +18,14 @@ public class EndingRoundEventArgs : IDeniableEvent /// /// Initializes a new instance of the class. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// public EndingRoundEventArgs(RoundSummary.LeadingTeam leadingTeam, RoundSummary.SumInfo_ClassList classList, bool isAllowed, bool isForceEnded) { ClassList = classList; LeadingTeam = (LeadingTeam)leadingTeam; - IsRoundEnded = isAllowed; IsForceEnded = isForceEnded; } @@ -49,22 +40,13 @@ public EndingRoundEventArgs(RoundSummary.LeadingTeam leadingTeam, RoundSummary.S public LeadingTeam LeadingTeam { get; set; } /// - /// Gets or sets a value indicating whether the round is going to finish or not. - /// - public bool IsRoundEnded { get; set; } // TODO: Obsolete this in Exiled 10 - - /// - /// Gets or Sets a value indicating whether the round is ended by API call. + /// Gets or sets a value indicating whether the round is ended by API call. /// public bool IsForceEnded { get; set; } /// /// Gets or sets a value indicating whether the event can be executed or not. /// - public bool IsAllowed - { - get => IsRoundEnded; - set => IsRoundEnded = value; - } -} + public bool IsAllowed { get; set; } + } } \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/PreAssigningHumanRolesEventArgs.cs b/Exiled.Events/EventArgs/Server/PreAssigningHumanRolesEventArgs.cs new file mode 100644 index 0000000000..a2a5b706c0 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/PreAssigningHumanRolesEventArgs.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using API.Enums; + + using Interfaces; + + using PlayerRoles; + + /// + /// Contains all information before setting up the environment for the assignment of human roles. + /// + public class PreAssigningHumanRolesEventArgs : IExiledEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public PreAssigningHumanRolesEventArgs(Team[] queue, int queueLength) + { + Queue = queue; + QueueLength = queueLength; + } + + /// + /// Gets or sets the human queue. + /// + public Team[] Queue { get; set; } + + /// + /// Gets or sets the human queue length. + /// + public int QueueLength { get; set; } + + /// + public bool IsAllowed { get; set; } = true; + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/PreAssigningScpRolesEventArgs.cs b/Exiled.Events/EventArgs/Server/PreAssigningScpRolesEventArgs.cs new file mode 100644 index 0000000000..eb5dbccf29 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/PreAssigningScpRolesEventArgs.cs @@ -0,0 +1,35 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using API.Enums; + + using Interfaces; + + using PlayerRoles; + + /// + /// Contains all information before setting up the environment for the assignment of SCP roles. + /// + public class PreAssigningScpRolesEventArgs : IExiledEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + public PreAssigningScpRolesEventArgs(int targetScpNumber) => Amount = targetScpNumber; + + /// + /// Gets or sets the amount of SCPs to be spawned. + /// + public int Amount { get; set; } + + /// + public bool IsAllowed { get; set; } = true; + } +} \ No newline at end of file diff --git a/Exiled.Events/EventArgs/Server/PreRespawningTeamEventArgs.cs b/Exiled.Events/EventArgs/Server/PreRespawningTeamEventArgs.cs new file mode 100644 index 0000000000..c565e98724 --- /dev/null +++ b/Exiled.Events/EventArgs/Server/PreRespawningTeamEventArgs.cs @@ -0,0 +1,77 @@ +// ----------------------------------------------------------------------- +// +// 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 Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + + using PlayerRoles; + + using Respawning; + + /// + /// Contains all information before setting up the environment for respawning a wave of or + /// . + /// + public class PreRespawningTeamEventArgs : IDeniableEvent + { + private SpawnableTeamType nextKnownTeam; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public PreRespawningTeamEventArgs(SpawnableTeamHandlerBase spawnableTeamHandler, int maxRespawn, SpawnableTeamType nextKnownTeam, bool isAllowed = true) + { + SpawnableTeamHandler = spawnableTeamHandler; + MaxWaveSize = maxRespawn; + this.nextKnownTeam = nextKnownTeam; + IsAllowed = isAllowed; + } + + /// + /// Gets or sets the . + /// + public SpawnableTeamHandlerBase SpawnableTeamHandler { get; set; } + + /// + /// Gets or sets the maximum amount of respawnable players. + /// + public int MaxWaveSize { get; set; } + + /// + /// Gets or sets a value indicating what the next respawnable team is. + /// + public SpawnableTeamType NextKnownTeam + { + get => nextKnownTeam; + set + { + nextKnownTeam = value; + + if (!RespawnManager.SpawnableTeams.TryGetValue(value, out SpawnableTeamHandlerBase spawnableTeam)) + { + MaxWaveSize = 0; + return; + } + + MaxWaveSize = spawnableTeam.MaxWaveSize; + } + } + + /// + /// Gets or sets a value indicating whether or not the spawn can occur. + /// + public bool IsAllowed { get; set; } + } +} diff --git a/Exiled.Events/EventArgs/Server/RespawningTeamEventArgs.cs b/Exiled.Events/EventArgs/Server/RespawningTeamEventArgs.cs index 0ea3641b53..b1b250c6b9 100644 --- a/Exiled.Events/EventArgs/Server/RespawningTeamEventArgs.cs +++ b/Exiled.Events/EventArgs/Server/RespawningTeamEventArgs.cs @@ -22,32 +22,19 @@ namespace Exiled.Events.EventArgs.Server /// public class RespawningTeamEventArgs : IDeniableEvent { - private SpawnableTeamType nextKnownTeam; - private int maximumRespawnAmount; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// public RespawningTeamEventArgs(List players, int maxRespawn, SpawnableTeamType nextKnownTeam, bool isAllowed = true) { Players = players; - MaximumRespawnAmount = maxRespawn; - - this.nextKnownTeam = nextKnownTeam; + MaxWaveSize = maxRespawn; + NextKnownTeam = nextKnownTeam; SpawnQueue = new(); - SpawnableTeam.GenerateQueue(SpawnQueue, players.Count); IsAllowed = isAllowed; } @@ -57,59 +44,23 @@ public RespawningTeamEventArgs(List players, int maxRespawn, SpawnableTe public List Players { get; } /// - /// Gets or sets the maximum amount of respawnable players. + /// Gets a value the next team to be respawned. /// - public int MaximumRespawnAmount - { - get => maximumRespawnAmount; - set - { - if (value < maximumRespawnAmount) - { - if (Players.Count > value) - Players.RemoveRange(value, Players.Count - value); - } - - maximumRespawnAmount = value; - } - } + public SpawnableTeamType NextKnownTeam { get; } /// - /// Gets or sets a value indicating what the next respawnable team is. + /// Gets the maximum amount of respawnable players. /// - public SpawnableTeamType NextKnownTeam - { - get => nextKnownTeam; - set - { - nextKnownTeam = value; - - if (!RespawnManager.SpawnableTeams.TryGetValue(value, out SpawnableTeamHandlerBase spawnableTeam)) - { - MaximumRespawnAmount = 0; - return; - } - - MaximumRespawnAmount = spawnableTeam.MaxWaveSize; - if (RespawnManager.SpawnableTeams.TryGetValue(nextKnownTeam, out SpawnableTeamHandlerBase @base)) - @base.GenerateQueue(SpawnQueue, Players.Count); - } - } + public int MaxWaveSize { get; } /// - /// Gets the current spawnable team. + /// Gets or sets the RoleTypeId spawn queue. /// - public SpawnableTeamHandlerBase SpawnableTeam - => RespawnManager.SpawnableTeams.TryGetValue(NextKnownTeam, out SpawnableTeamHandlerBase @base) ? @base : null; + public Queue SpawnQueue { get; set; } /// /// Gets or sets a value indicating whether or not the spawn can occur. /// public bool IsAllowed { get; set; } - - /// - /// Gets or sets the RoleTypeId spawn queue. - /// - public Queue SpawnQueue { get; set; } } } diff --git a/Exiled.Events/EventArgs/Server/RestartedRespawnSequenceEventArgs.cs b/Exiled.Events/EventArgs/Server/RestartedRespawnSequenceEventArgs.cs new file mode 100644 index 0000000000..afb52115aa --- /dev/null +++ b/Exiled.Events/EventArgs/Server/RestartedRespawnSequenceEventArgs.cs @@ -0,0 +1,65 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Server +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + using API.Enums; + using API.Features; + using Interfaces; + using PlayerRoles; + using PlayerRoles.RoleAssign; + using Respawning; + + /// + /// Contains all information after a new respawn sequence has been restarted. + /// + public class RestartedRespawnSequenceEventArgs : IExiledEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + public RestartedRespawnSequenceEventArgs(RespawnManager respawnManager) => RespawnManager = respawnManager; + + /// + /// Gets the instance. + /// + public RespawnManager RespawnManager { get; } + + /// + /// Gets the sequence's timer. + /// + public Stopwatch Timer => RespawnManager._stopwatch; + + /// + /// Gets or sets the current . + /// + public RespawnManager.RespawnSequencePhase CurrentRespawnSequencePhase + { + get => RespawnManager._curSequence; + set => RespawnManager._curSequence = value; + } + + /// + /// Gets or sets the time for the next sequence. + /// + public float TimeForNextSequence + { + get => RespawnManager.TimeTillRespawn; + set + { + RespawnManager._timeForNextSequence = value; + Timer.Restart(); + } + } + } +} \ No newline at end of file diff --git a/Exiled.Events/Features/Patcher.cs b/Exiled.Events/Features/Patcher.cs index d22c29c7c1..b2a7eed04e 100644 --- a/Exiled.Events/Features/Patcher.cs +++ b/Exiled.Events/Features/Patcher.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Features using System.Reflection; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Interfaces; using HarmonyLib; diff --git a/Exiled.Events/Handlers/Internal/MapGenerated.cs b/Exiled.Events/Handlers/Internal/MapGenerated.cs index d4bd8d037a..290a6d58e2 100644 --- a/Exiled.Events/Handlers/Internal/MapGenerated.cs +++ b/Exiled.Events/Handlers/Internal/MapGenerated.cs @@ -12,8 +12,8 @@ namespace Exiled.Events.Handlers.Internal using System.Linq; using API.Features; + using API.Features.Core.Generic.Pools; using API.Features.Items; - using API.Features.Pools; using API.Structs; using Exiled.API.Enums; @@ -47,6 +47,11 @@ public static void OnMapGenerated() { Map.ClearCache(); + // TODO: Fix For (https://trello.com/c/cUwpZDLs/5003-config-teamrespawnqueue-in-configgameplay-is-not-working-as-expected) + 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); + GenerateAttachments(); Timing.CallDelayed(1, GenerateCache); } diff --git a/Exiled.Events/Handlers/Internal/Round.cs b/Exiled.Events/Handlers/Internal/Round.cs index 48ddc98546..3c17fbd58b 100644 --- a/Exiled.Events/Handlers/Internal/Round.cs +++ b/Exiled.Events/Handlers/Internal/Round.cs @@ -7,14 +7,16 @@ namespace Exiled.Events.Handlers.Internal { + using System.Linq; + using CentralAuth; + using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Roles; using Exiled.Events.EventArgs.Player; using Exiled.Events.EventArgs.Scp049; using Exiled.Loader; using Exiled.Loader.Features; - using InventorySystem; using InventorySystem.Items.Usables; using PlayerRoles; @@ -83,6 +85,19 @@ public static void OnActivatingSense(ActivatingSenseEventArgs ev) public static void OnVerified(VerifiedEventArgs ev) { RoleAssigner.CheckLateJoin(ev.Player.ReferenceHub, ClientInstanceMode.ReadyClient); + + foreach (Player player in Player.List) + { + if (player.Role is FpcRole { FakeAppearance: not null } fpcRole) + player.ChangeAppearance(fpcRole.FakeAppearance.Value, new[] { ev.Player }); + } + + // TODO: Remove if this has been fixed for https://trello.com/c/CzPD304L/5983-networking-blackout-is-not-synchronized-for-the-new-players + foreach (Room room in Room.Get(current => current.AreLightsOff)) + { + ev.Player.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkLightsEnabled), true); + ev.Player.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkLightsEnabled), false); + } } } } diff --git a/Exiled.Events/Handlers/Map.cs b/Exiled.Events/Handlers/Map.cs index 561a2f9b83..120f0cbd7a 100644 --- a/Exiled.Events/Handlers/Map.cs +++ b/Exiled.Events/Handlers/Map.cs @@ -110,6 +110,11 @@ public static class Map /// public static Event Scp244Spawning { get; set; } = new(); + /// + /// Invoked before an item is placed in the pocket dimension. + /// + public static Event PlacingPickupIntoPocketDimension { get; set; } = new(); + /// /// Called before placing a decal. /// @@ -216,5 +221,10 @@ public static class Map /// /// The instance. public static void OnScp244Spawning(Scp244SpawningEventArgs ev) => Scp244Spawning.InvokeSafely(ev); + + /// Called before an item is dropped in the pocket dimension. + /// + /// The instnace. + public static void OnPlacingPickupIntoPocketDimension(PlacingPickupIntoPocketDimensionEventArgs ev) => PlacingPickupIntoPocketDimension.InvokeSafely(ev); } } \ No newline at end of file diff --git a/Exiled.Events/Handlers/Player.cs b/Exiled.Events/Handlers/Player.cs index 5d2c161068..bd66fa0798 100644 --- a/Exiled.Events/Handlers/Player.cs +++ b/Exiled.Events/Handlers/Player.cs @@ -522,6 +522,11 @@ public class Player /// public static Event ChangingNickname { get; set; } = new(); + /// + /// Invoked before displaying the hitmarker to the player. + /// + public static Event ShowingHitMarker { get; set; } = new(); + /// /// Called before reserved slot is resolved for a . /// @@ -1112,6 +1117,12 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// The instance. public static void OnChangingNickname(ChangingNicknameEventArgs ev) => ChangingNickname.InvokeSafely(ev); + /// + /// Called before displaying the hitmarker to the player. + /// + /// The instance. + public static void OnShowingHitMarker(DisplayingHitmarkerEventArgs ev) => ShowingHitMarker.InvokeSafely(ev); + /// /// Called before pre-authenticating a . /// diff --git a/Exiled.Events/Handlers/Scp173.cs b/Exiled.Events/Handlers/Scp173.cs index bf844714b9..a3d69e8ef4 100644 --- a/Exiled.Events/Handlers/Scp173.cs +++ b/Exiled.Events/Handlers/Scp173.cs @@ -37,6 +37,16 @@ public static class Scp173 /// public static Event UsingBreakneckSpeeds { get; set; } = new(); + /// + /// Invoked before a player starts looking at SCP-173. + /// + public static Event AddingObserver { get; set; } = new(); + + /// + /// Invoked after a player stops looking at SCP-173. + /// + public static Event RemoveObserver { get; set; } = new(); + /// /// Called before players near SCP-173 blink. /// @@ -60,5 +70,17 @@ public static class Scp173 /// /// The instance. public static void OnUsingBreakneckSpeeds(UsingBreakneckSpeedsEventArgs ev) => UsingBreakneckSpeeds.InvokeSafely(ev); + + /// + /// Called before player starts looking at SCP-173. + /// + /// The instance. + public static void OnAddingObserver(AddingObserverEventArgs ev) => AddingObserver.InvokeSafely(ev); + + /// + /// Called after a player stops looking at SCP-173. + /// + /// The instance. + public static void OnRemoveObserver(RemoveObserverEventArgs ev) => RemoveObserver.InvokeSafely(ev); } } \ No newline at end of file diff --git a/Exiled.Events/Handlers/Server.cs b/Exiled.Events/Handlers/Server.cs index 6e71bf2672..8493739a1c 100644 --- a/Exiled.Events/Handlers/Server.cs +++ b/Exiled.Events/Handlers/Server.cs @@ -52,6 +52,11 @@ public static class Server /// public static Event ReportingCheater { get; set; } = new(); + /// + /// Invoked before setting up the environment for respawning a wave of Chaos Insurgency or NTF. + /// + public static Event PreRespawningTeam { get; set; } = new(); + /// /// Invoked before respawning a wave of Chaos Insurgency or NTF. /// @@ -68,7 +73,7 @@ public static class Server public static Event LocalReporting { get; set; } = new(); /// - /// Invoked before choosing the Team than player will get. + /// Invoked before choosing the team to be assigned to a player. /// public static Event ChoosingStartTeamQueue { get; set; } = new(); @@ -112,6 +117,46 @@ public static class Server /// public static Event RespawnedTeam { get; set; } = new(); + /// + /// Invoked before setting up the environment for the assignment of human roles. + /// + public static Event PreAssigningHumanRoles { get; set; } = new(); + + /// + /// Invoked before setting up the environment for the assignment of SCP roles. + /// + public static Event PreAssigningScpRoles { get; set; } = new(); + + /// + /// Invoked before assigning human roles. + /// + public static Event AssigningHumanRoles { get; set; } = new(); + + /// + /// Invoked before assigning SCP roles. + /// + public static Event AssigningScpRoles { get; set; } = new(); + + /// + /// Invoked before deploying a SCP role. + /// + public static Event DeployingScpRole { get; set; } = new(); + + /// + /// Invoked before deploying a human role. + /// + public static Event DeployingHumanRole { get; set; } = new(); + + /// + /// Invoked before deploying a team role. + /// + public static Event DeployingTeamRole { get; set; } = new(); + + /// + /// Invoked after a new respawn sequence has been restarted. + /// + public static Event RestartedRespawnSequence { get; set; } = new(); + /// /// Called before waiting for players. /// @@ -145,12 +190,31 @@ public static class Server /// The instance. public static void OnReportingCheater(ReportingCheaterEventArgs ev) => ReportingCheater.InvokeSafely(ev); + /// + /// Called before selecting the team that will respawn next. + /// + /// The instance. + public static void OnSelectingRespawnTeam(SelectingRespawnTeamEventArgs ev) => SelectingRespawnTeam.InvokeSafely(ev); + + /// + /// Called before setting up the environment for respawning a wave of Chaos Insurgency or NTF. + /// + /// The instance. + public static void OnPreRespawningTeam(PreRespawningTeamEventArgs ev) => PreRespawningTeam.InvokeSafely(ev); + /// /// Called before respawning a wave of Chaos Insurgency or NTF. /// /// The instance. public static void OnRespawningTeam(RespawningTeamEventArgs ev) => RespawningTeam.InvokeSafely(ev); + /// + /// Called after a team has spawned. + /// + /// + /// + public static void OnRespawnedTeam(SpawnableTeamType teamType, List hubs) => RespawnedTeam.InvokeSafely(new RespawnedTeamEventArgs(teamType, hubs)); + /// /// Called before adding an unit name. /// @@ -164,7 +228,7 @@ public static class Server public static void OnLocalReporting(LocalReportingEventArgs ev) => LocalReporting.InvokeSafely(ev); /// - /// Called before a 's custom display name is changed. + /// Called before choosing the team to be assigned to a player. /// /// The instance. public static void OnChoosingStartTeam(ChoosingStartTeamQueueEventArgs ev) => ChoosingStartTeamQueue.InvokeSafely(ev); @@ -200,16 +264,51 @@ public static class Server public static void OnReloadedPermissions() => ReloadedPermissions.InvokeSafely(); /// - /// Called before selecting the team that will respawn next. + /// Called before setting up the environment for the assignment of human roles. /// - /// The instance. - public static void OnSelectingRespawnTeam(SelectingRespawnTeamEventArgs ev) => SelectingRespawnTeam.InvokeSafely(ev); + /// The instance. + public static void OnPreAssigningHumanRoles(PreAssigningHumanRolesEventArgs ev) => PreAssigningHumanRoles.InvokeSafely(ev); /// - /// Called after a team has spawned. + /// Called before setting up the environment for the assignment of SCP roles. /// - /// - /// - public static void OnRespawnedTeam(SpawnableTeamType teamType, List hubs) => RespawnedTeam.InvokeSafely(new RespawnedTeamEventArgs(teamType, hubs)); + /// The instance. + public static void OnPreAssigningScpRoles(PreAssigningScpRolesEventArgs ev) => PreAssigningScpRoles.InvokeSafely(ev); + + /// + /// Called before assigning human roles. + /// + /// The instance. + public static void OnAssigningHumanRoles(AssigningHumanRolesEventArgs ev) => AssigningHumanRoles.InvokeSafely(ev); + + /// + /// Called before assigning SCP roles. + /// + /// The instance. + public static void OnAssigningScpRoles(AssigningScpRolesEventArgs ev) => AssigningScpRoles.InvokeSafely(ev); + + /// + /// Called before deploying a SCP role. + /// + /// The instance. + public static void OnDeployingScpRole(DeployingScpRoleEventArgs ev) => DeployingScpRole.InvokeSafely(ev); + + /// + /// Called before deploying a human role. + /// + /// The instance. + public static void OnDeployingHumanRole(DeployingHumanRoleEventArgs ev) => DeployingHumanRole.InvokeSafely(ev); + + /// + /// Called before deploying a team role. + /// + /// The instance. + public static void OnDeployingTeamRole(DeployingTeamRoleEventArgs ev) => DeployingTeamRole.InvokeSafely(ev); + + /// + /// Called after a new respawn sequence has been restarted. + /// + /// The instance. + public static void OnRestartedRespawnSequence(RestartedRespawnSequenceEventArgs ev) => RestartedRespawnSequence.InvokeSafely(ev); } } \ No newline at end of file diff --git a/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs b/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs index 9c49d44972..379810aec6 100644 --- a/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs +++ b/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Cassie using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Cassie; diff --git a/Exiled.Events/Patches/Events/Item/ChangingAmmo.cs b/Exiled.Events/Patches/Events/Item/ChangingAmmo.cs index e90d812c2e..2baf3ca022 100644 --- a/Exiled.Events/Patches/Events/Item/ChangingAmmo.cs +++ b/Exiled.Events/Patches/Events/Item/ChangingAmmo.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Item using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Item; diff --git a/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs b/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs index 3e49274a00..afb30191c2 100644 --- a/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs +++ b/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs @@ -11,8 +11,8 @@ namespace Exiled.Events.Patches.Events.Item using System.Reflection.Emit; using API.Features; + using API.Features.Core.Generic.Pools; using API.Features.Items; - using API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Item; diff --git a/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs b/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs index 97c4f4d23f..e5a9291e9e 100644 --- a/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs +++ b/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Item using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Item/ReceivingPreference.cs b/Exiled.Events/Patches/Events/Item/ReceivingPreference.cs index 6d845fffa3..fda3a6380f 100644 --- a/Exiled.Events/Patches/Events/Item/ReceivingPreference.cs +++ b/Exiled.Events/Patches/Events/Item/ReceivingPreference.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Item using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Item; diff --git a/Exiled.Events/Patches/Events/Item/UsingRadioPickupBattery.cs b/Exiled.Events/Patches/Events/Item/UsingRadioPickupBattery.cs index d257ff9738..2ffa5b7a6e 100644 --- a/Exiled.Events/Patches/Events/Item/UsingRadioPickupBattery.cs +++ b/Exiled.Events/Patches/Events/Item/UsingRadioPickupBattery.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Item using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Item; using HarmonyLib; using InventorySystem.Items.Radio; diff --git a/Exiled.Events/Patches/Events/Map/AnnouncingDecontamination.cs b/Exiled.Events/Patches/Events/Map/AnnouncingDecontamination.cs index 14a78a7676..786feec93b 100644 --- a/Exiled.Events/Patches/Events/Map/AnnouncingDecontamination.cs +++ b/Exiled.Events/Patches/Events/Map/AnnouncingDecontamination.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Map using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; diff --git a/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs b/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs index 484516c7f7..5d9c3070c9 100644 --- a/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs +++ b/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Map using System.Reflection.Emit; using System.Text.RegularExpressions; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; diff --git a/Exiled.Events/Patches/Events/Map/AnnouncingScpTermination.cs b/Exiled.Events/Patches/Events/Map/AnnouncingScpTermination.cs index b442452a37..33a077fed3 100644 --- a/Exiled.Events/Patches/Events/Map/AnnouncingScpTermination.cs +++ b/Exiled.Events/Patches/Events/Map/AnnouncingScpTermination.cs @@ -10,8 +10,8 @@ namespace Exiled.Events.Patches.Events.Map using System.Collections.Generic; using System.Reflection.Emit; + using API.Features.Core.Generic.Pools; using API.Features.DamageHandlers; - using API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Map/BreakingScp2176.cs b/Exiled.Events/Patches/Events/Map/BreakingScp2176.cs index 9b0c2cb5c5..683f803fa2 100644 --- a/Exiled.Events/Patches/Events/Map/BreakingScp2176.cs +++ b/Exiled.Events/Patches/Events/Map/BreakingScp2176.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Map using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; using Footprinting; diff --git a/Exiled.Events/Patches/Events/Map/ChangingIntoGrenade.cs b/Exiled.Events/Patches/Events/Map/ChangingIntoGrenade.cs index 5b806d5b10..3bb943e08d 100644 --- a/Exiled.Events/Patches/Events/Map/ChangingIntoGrenade.cs +++ b/Exiled.Events/Patches/Events/Map/ChangingIntoGrenade.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Map using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; @@ -118,7 +118,7 @@ private static IEnumerable Transpiler(IEnumerable +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Map +{ + using System.Collections.Generic; + + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Map; + using Handlers; + using HarmonyLib; + using InventorySystem.Items.Pickups; + using Mirror; + using PlayerRoles.PlayableScps.Scp106; + + using static PlayerRoles.PlayableScps.Scp106.Scp106PocketItemManager; + + /// + /// Patches + /// Adds the event. + /// + [EventPatch(typeof(Map), nameof(Map.PlacingPickupIntoPocketDimension))] + [HarmonyPatch(typeof(Scp106PocketItemManager), nameof(Scp106PocketItemManager.OnAdded))] + internal static class PlacingPickupIntoPocketDimension + { + private static void Postfix(ItemPickupBase ipb) + { + if (TrackedItems.TryGetValue(ipb, out PocketItem pocketItem)) + { + PlacingPickupIntoPocketDimensionEventArgs ev = new(ipb, pocketItem, true); + Map.OnPlacingPickupIntoPocketDimension(ev); + + if (!ev.IsAllowed) + { + TrackedItems.Remove(ipb); + } + } + } + } +} diff --git a/Exiled.Events/Patches/Events/Map/SpawningItem.cs b/Exiled.Events/Patches/Events/Map/SpawningItem.cs index 2d154875ea..e385cc674c 100644 --- a/Exiled.Events/Patches/Events/Map/SpawningItem.cs +++ b/Exiled.Events/Patches/Events/Map/SpawningItem.cs @@ -10,8 +10,8 @@ namespace Exiled.Events.Patches.Events.Map using System.Collections.Generic; using System.Reflection.Emit; + using API.Features.Core.Generic.Pools; using API.Features.Doors; - using API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; diff --git a/Exiled.Events/Patches/Events/Map/SpawningTeamVehicle.cs b/Exiled.Events/Patches/Events/Map/SpawningTeamVehicle.cs index 63f0f8fe04..c26d066b39 100644 --- a/Exiled.Events/Patches/Events/Map/SpawningTeamVehicle.cs +++ b/Exiled.Events/Patches/Events/Map/SpawningTeamVehicle.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Map using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; diff --git a/Exiled.Events/Patches/Events/Map/TurningOffLights.cs b/Exiled.Events/Patches/Events/Map/TurningOffLights.cs index f8dbb2f634..438dcc8f4c 100644 --- a/Exiled.Events/Patches/Events/Map/TurningOffLights.cs +++ b/Exiled.Events/Patches/Events/Map/TurningOffLights.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Map using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Map; diff --git a/Exiled.Events/Patches/Events/Player/ActivatingWarheadPanel.cs b/Exiled.Events/Patches/Events/Player/ActivatingWarheadPanel.cs index 4a3d790b19..37d710493d 100644 --- a/Exiled.Events/Patches/Events/Player/ActivatingWarheadPanel.cs +++ b/Exiled.Events/Patches/Events/Player/ActivatingWarheadPanel.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Enums; using Exiled.API.Features.Items; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Player/ActivatingWorkstation.cs b/Exiled.Events/Patches/Events/Player/ActivatingWorkstation.cs index 4ee58f8ecd..5f56acf146 100644 --- a/Exiled.Events/Patches/Events/Player/ActivatingWorkstation.cs +++ b/Exiled.Events/Patches/Events/Player/ActivatingWorkstation.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/AddingItem.cs b/Exiled.Events/Patches/Events/Player/AddingItem.cs index fb9f3457c1..f6b1914322 100644 --- a/Exiled.Events/Patches/Events/Player/AddingItem.cs +++ b/Exiled.Events/Patches/Events/Player/AddingItem.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using Exiled.API.Features.Items; using Exiled.API.Features.Pickups; diff --git a/Exiled.Events/Patches/Events/Player/Banned.cs b/Exiled.Events/Patches/Events/Player/Banned.cs index d06cdcf2f2..c2934279f2 100644 --- a/Exiled.Events/Patches/Events/Player/Banned.cs +++ b/Exiled.Events/Patches/Events/Player/Banned.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Banning.cs b/Exiled.Events/Patches/Events/Player/Banning.cs index 9873b079bb..088780662a 100644 --- a/Exiled.Events/Patches/Events/Player/Banning.cs +++ b/Exiled.Events/Patches/Events/Player/Banning.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using API.Features; using CommandSystem; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using Footprinting; diff --git a/Exiled.Events/Patches/Events/Player/ChangedItem.cs b/Exiled.Events/Patches/Events/Player/ChangedItem.cs index 1fe58b8765..0556fe8ff3 100644 --- a/Exiled.Events/Patches/Events/Player/ChangedItem.cs +++ b/Exiled.Events/Patches/Events/Player/ChangedItem.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingGroup.cs b/Exiled.Events/Patches/Events/Player/ChangingGroup.cs index cf179dfc5d..92acc81494 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingGroup.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingGroup.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingItem.cs b/Exiled.Events/Patches/Events/Player/ChangingItem.cs index 323c7c1b17..527eea22cc 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingItem.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingItem.cs @@ -11,8 +11,8 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; + using API.Features.Core.Generic.Pools; using API.Features.Items; - using API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingMicroHIDState.cs b/Exiled.Events/Patches/Events/Player/ChangingMicroHIDState.cs index e476e2da95..38ce84e884 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingMicroHIDState.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingMicroHIDState.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingMoveState.cs b/Exiled.Events/Patches/Events/Player/ChangingMoveState.cs index 61d05c0aa7..1c11ad5332 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingMoveState.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingMoveState.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Player/ChangingNickname.cs b/Exiled.Events/Patches/Events/Player/ChangingNickname.cs index 3af9b86337..6e4e16468f 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingNickname.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingNickname.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingRadioPreset.cs b/Exiled.Events/Patches/Events/Player/ChangingRadioPreset.cs index 1199215c81..bf9ab3da62 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingRadioPreset.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingRadioPreset.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingRoleAndSpawned.cs b/Exiled.Events/Patches/Events/Player/ChangingRoleAndSpawned.cs index 786842b5bc..c4e52dec3d 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingRoleAndSpawned.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingRoleAndSpawned.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using API.Features.Roles; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ChangingSpectatedPlayerPatch.cs b/Exiled.Events/Patches/Events/Player/ChangingSpectatedPlayerPatch.cs index c7f14f9553..028b35fec8 100644 --- a/Exiled.Events/Patches/Events/Player/ChangingSpectatedPlayerPatch.cs +++ b/Exiled.Events/Patches/Events/Player/ChangingSpectatedPlayerPatch.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/DamagingDoor.cs b/Exiled.Events/Patches/Events/Player/DamagingDoor.cs index 9fb8aa9364..9427ffe58c 100644 --- a/Exiled.Events/Patches/Events/Player/DamagingDoor.cs +++ b/Exiled.Events/Patches/Events/Player/DamagingDoor.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/DamagingShootingTarget.cs b/Exiled.Events/Patches/Events/Player/DamagingShootingTarget.cs index 9b73611eb9..40d6f7a455 100644 --- a/Exiled.Events/Patches/Events/Player/DamagingShootingTarget.cs +++ b/Exiled.Events/Patches/Events/Player/DamagingShootingTarget.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Events.Player using AdminToys; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/DamagingWindow.cs b/Exiled.Events/Patches/Events/Player/DamagingWindow.cs index fe342e84c3..ea5a3d457b 100644 --- a/Exiled.Events/Patches/Events/Player/DamagingWindow.cs +++ b/Exiled.Events/Patches/Events/Player/DamagingWindow.cs @@ -10,8 +10,8 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; + using API.Features.Core.Generic.Pools; using API.Features.DamageHandlers; - using API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/DeactivatingWorkstation.cs b/Exiled.Events/Patches/Events/Player/DeactivatingWorkstation.cs index eee14bf646..76d3804a11 100644 --- a/Exiled.Events/Patches/Events/Player/DeactivatingWorkstation.cs +++ b/Exiled.Events/Patches/Events/Player/DeactivatingWorkstation.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Destroying.cs b/Exiled.Events/Patches/Events/Player/Destroying.cs index ad1d84480c..2109a62370 100644 --- a/Exiled.Events/Patches/Events/Player/Destroying.cs +++ b/Exiled.Events/Patches/Events/Player/Destroying.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Runtime.CompilerServices; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs b/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs index c67b70d030..f0da4139ca 100644 --- a/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs +++ b/Exiled.Events/Patches/Events/Player/DroppingAmmo.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/DroppingItem.cs b/Exiled.Events/Patches/Events/Player/DroppingItem.cs index 0e03115329..fed2a45d27 100644 --- a/Exiled.Events/Patches/Events/Player/DroppingItem.cs +++ b/Exiled.Events/Patches/Events/Player/DroppingItem.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features.Pickups; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/DyingAndDied.cs b/Exiled.Events/Patches/Events/Player/DyingAndDied.cs index ea11377759..ed98eb79e6 100644 --- a/Exiled.Events/Patches/Events/Player/DyingAndDied.cs +++ b/Exiled.Events/Patches/Events/Player/DyingAndDied.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using API.Features.Roles; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/EarningAchievement.cs b/Exiled.Events/Patches/Events/Player/EarningAchievement.cs index a818a39863..b0e622a234 100644 --- a/Exiled.Events/Patches/Events/Player/EarningAchievement.cs +++ b/Exiled.Events/Patches/Events/Player/EarningAchievement.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Events.Player using Achievements; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/EnteringKillerCollision.cs b/Exiled.Events/Patches/Events/Player/EnteringKillerCollision.cs index 3f277f3ad2..842e3890cd 100644 --- a/Exiled.Events/Patches/Events/Player/EnteringKillerCollision.cs +++ b/Exiled.Events/Patches/Events/Player/EnteringKillerCollision.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/EnteringPocketDimension.cs b/Exiled.Events/Patches/Events/Player/EnteringPocketDimension.cs index 1dabf6e26e..18ef52fe23 100644 --- a/Exiled.Events/Patches/Events/Player/EnteringPocketDimension.cs +++ b/Exiled.Events/Patches/Events/Player/EnteringPocketDimension.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/EnteringSinkholeEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/EnteringSinkholeEnvironmentalHazard.cs index 2bccead6f4..a231697fbb 100644 --- a/Exiled.Events/Patches/Events/Player/EnteringSinkholeEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/EnteringSinkholeEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/EnteringTantrumEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/EnteringTantrumEnvironmentalHazard.cs index faea5e7787..2325836032 100644 --- a/Exiled.Events/Patches/Events/Player/EnteringTantrumEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/EnteringTantrumEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Escaping.cs b/Exiled.Events/Patches/Events/Player/Escaping.cs index ca6213cddb..120d4ce7a8 100644 --- a/Exiled.Events/Patches/Events/Player/Escaping.cs +++ b/Exiled.Events/Patches/Events/Player/Escaping.cs @@ -17,7 +17,7 @@ namespace Exiled.Events.Patches.Events.Player using API.Enums; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using EventArgs.Player; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Player/EscapingPocketDimension.cs b/Exiled.Events/Patches/Events/Player/EscapingPocketDimension.cs index b1db281b4d..401faf6651 100644 --- a/Exiled.Events/Patches/Events/Player/EscapingPocketDimension.cs +++ b/Exiled.Events/Patches/Events/Player/EscapingPocketDimension.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ExitingSinkholeEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/ExitingSinkholeEnvironmentalHazard.cs index 6c8099277d..a77133d850 100644 --- a/Exiled.Events/Patches/Events/Player/ExitingSinkholeEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/ExitingSinkholeEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ExitingTantrumEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/ExitingTantrumEnvironmentalHazard.cs index d7d0e13af3..0756a3f545 100644 --- a/Exiled.Events/Patches/Events/Player/ExitingTantrumEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/ExitingTantrumEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/FailingEscapePocketDimension.cs b/Exiled.Events/Patches/Events/Player/FailingEscapePocketDimension.cs index f8b08b74a3..a183bed216 100644 --- a/Exiled.Events/Patches/Events/Player/FailingEscapePocketDimension.cs +++ b/Exiled.Events/Patches/Events/Player/FailingEscapePocketDimension.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs b/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs index 6ebd931f4e..5a6b90e439 100644 --- a/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs +++ b/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features.Items; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Player/FlippingCoin.cs b/Exiled.Events/Patches/Events/Player/FlippingCoin.cs index 96756f3545..de297a71a8 100644 --- a/Exiled.Events/Patches/Events/Player/FlippingCoin.cs +++ b/Exiled.Events/Patches/Events/Player/FlippingCoin.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/Hurting.cs b/Exiled.Events/Patches/Events/Player/Hurting.cs index 73e8ea4a45..1a8464f490 100644 --- a/Exiled.Events/Patches/Events/Player/Hurting.cs +++ b/Exiled.Events/Patches/Events/Player/Hurting.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using API.Features.Roles; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Interacted.cs b/Exiled.Events/Patches/Events/Player/Interacted.cs index d6e0d637ef..dee8e4c1db 100644 --- a/Exiled.Events/Patches/Events/Player/Interacted.cs +++ b/Exiled.Events/Patches/Events/Player/Interacted.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/InteractingDoor.cs b/Exiled.Events/Patches/Events/Player/InteractingDoor.cs index 6f491b8118..10ecb78c3f 100644 --- a/Exiled.Events/Patches/Events/Player/InteractingDoor.cs +++ b/Exiled.Events/Patches/Events/Player/InteractingDoor.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/InteractingElevator.cs b/Exiled.Events/Patches/Events/Player/InteractingElevator.cs index 1889f29dca..801cdfbad7 100644 --- a/Exiled.Events/Patches/Events/Player/InteractingElevator.cs +++ b/Exiled.Events/Patches/Events/Player/InteractingElevator.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/InteractingGenerator.cs b/Exiled.Events/Patches/Events/Player/InteractingGenerator.cs index 92ccee4cc3..4706c66824 100644 --- a/Exiled.Events/Patches/Events/Player/InteractingGenerator.cs +++ b/Exiled.Events/Patches/Events/Player/InteractingGenerator.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/InteractingLocker.cs b/Exiled.Events/Patches/Events/Player/InteractingLocker.cs index b706f067f9..e615eb98fc 100644 --- a/Exiled.Events/Patches/Events/Player/InteractingLocker.cs +++ b/Exiled.Events/Patches/Events/Player/InteractingLocker.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/InteractingShootingTarget.cs b/Exiled.Events/Patches/Events/Player/InteractingShootingTarget.cs index e2cc9661ab..472319f246 100644 --- a/Exiled.Events/Patches/Events/Player/InteractingShootingTarget.cs +++ b/Exiled.Events/Patches/Events/Player/InteractingShootingTarget.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/IntercomSpeaking.cs b/Exiled.Events/Patches/Events/Player/IntercomSpeaking.cs index 5079a776c1..c13d1aa4d9 100644 --- a/Exiled.Events/Patches/Events/Player/IntercomSpeaking.cs +++ b/Exiled.Events/Patches/Events/Player/IntercomSpeaking.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Diagnostics; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/IssuingMute.cs b/Exiled.Events/Patches/Events/Player/IssuingMute.cs index 7fd44db040..5a7f39fdbc 100644 --- a/Exiled.Events/Patches/Events/Player/IssuingMute.cs +++ b/Exiled.Events/Patches/Events/Player/IssuingMute.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Joined.cs b/Exiled.Events/Patches/Events/Player/Joined.cs index a97a5fe2de..35c942de4d 100644 --- a/Exiled.Events/Patches/Events/Player/Joined.cs +++ b/Exiled.Events/Patches/Events/Player/Joined.cs @@ -25,23 +25,6 @@ namespace Exiled.Events.Patches.Events.Player [HarmonyPatch(typeof(ReferenceHub), nameof(ReferenceHub.Start))] public static class Joined { - private static Type basePlayerType = typeof(Player); - - /// - /// Gets or sets the base type. - /// - public static Type BasePlayerType - { - get => basePlayerType; - set - { - if (!typeof(Player).IsAssignableFrom(value)) - return; - - basePlayerType = value; - } - } - internal static void CallEvent(ReferenceHub hub, out Player player) { try @@ -49,7 +32,7 @@ internal static void CallEvent(ReferenceHub hub, out Player player) #if DEBUG Log.Debug("Creating new player object"); #endif - player = Activator.CreateInstance(BasePlayerType, false, hub) as Player; + player = Activator.CreateInstance(Player.DEFAULT_PLAYER_CLASS, args: hub) as Player; #if DEBUG Log.Debug($"Object exists {player is not null}"); Log.Debug($"Creating player object for {hub.nicknameSync.Network_displayName}"); diff --git a/Exiled.Events/Patches/Events/Player/Jumping.cs b/Exiled.Events/Patches/Events/Player/Jumping.cs index 460867460e..52eeecaec0 100644 --- a/Exiled.Events/Patches/Events/Player/Jumping.cs +++ b/Exiled.Events/Patches/Events/Player/Jumping.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Player/Kicked.cs b/Exiled.Events/Patches/Events/Player/Kicked.cs index b3934543ca..072e47c74e 100644 --- a/Exiled.Events/Patches/Events/Player/Kicked.cs +++ b/Exiled.Events/Patches/Events/Player/Kicked.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Kicking.cs b/Exiled.Events/Patches/Events/Player/Kicking.cs index 0a9286933b..8dcfd2c00a 100644 --- a/Exiled.Events/Patches/Events/Player/Kicking.cs +++ b/Exiled.Events/Patches/Events/Player/Kicking.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using CommandSystem; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Landing.cs b/Exiled.Events/Patches/Events/Player/Landing.cs index 37c745a316..b51d667c58 100644 --- a/Exiled.Events/Patches/Events/Player/Landing.cs +++ b/Exiled.Events/Patches/Events/Player/Landing.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Player/Left.cs b/Exiled.Events/Patches/Events/Player/Left.cs index 2e808334db..fde95dbaae 100644 --- a/Exiled.Events/Patches/Events/Player/Left.cs +++ b/Exiled.Events/Patches/Events/Player/Left.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/MakingNoise.cs b/Exiled.Events/Patches/Events/Player/MakingNoise.cs index eb525afa58..912c7498db 100644 --- a/Exiled.Events/Patches/Events/Player/MakingNoise.cs +++ b/Exiled.Events/Patches/Events/Player/MakingNoise.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/PickingUp330.cs b/Exiled.Events/Patches/Events/Player/PickingUp330.cs index ab6dd6bab2..bbc1a725fe 100644 --- a/Exiled.Events/Patches/Events/Player/PickingUp330.cs +++ b/Exiled.Events/Patches/Events/Player/PickingUp330.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/PickingUpAmmo.cs b/Exiled.Events/Patches/Events/Player/PickingUpAmmo.cs index c548bdd6d5..47d91bb7ea 100644 --- a/Exiled.Events/Patches/Events/Player/PickingUpAmmo.cs +++ b/Exiled.Events/Patches/Events/Player/PickingUpAmmo.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/PickingUpArmor.cs b/Exiled.Events/Patches/Events/Player/PickingUpArmor.cs index baccd89d6c..2518e04a96 100644 --- a/Exiled.Events/Patches/Events/Player/PickingUpArmor.cs +++ b/Exiled.Events/Patches/Events/Player/PickingUpArmor.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/PickingUpItem.cs b/Exiled.Events/Patches/Events/Player/PickingUpItem.cs index 78e3611591..160fb7ad6d 100644 --- a/Exiled.Events/Patches/Events/Player/PickingUpItem.cs +++ b/Exiled.Events/Patches/Events/Player/PickingUpItem.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/PickingUpScp244.cs b/Exiled.Events/Patches/Events/Player/PickingUpScp244.cs index 13f634563c..57161773bf 100644 --- a/Exiled.Events/Patches/Events/Player/PickingUpScp244.cs +++ b/Exiled.Events/Patches/Events/Player/PickingUpScp244.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs b/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs index bd83c19e9e..7ad35ff1d2 100644 --- a/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs +++ b/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs b/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs index 3689272e8d..b1b5d9c783 100644 --- a/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs +++ b/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using CustomPlayerEffects; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Player/RemovingItem.cs b/Exiled.Events/Patches/Events/Player/RemovingItem.cs index 31bfeb119e..f4292067b1 100644 --- a/Exiled.Events/Patches/Events/Player/RemovingItem.cs +++ b/Exiled.Events/Patches/Events/Player/RemovingItem.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using Exiled.API.Features.Items; using Exiled.API.Features.Pickups; diff --git a/Exiled.Events/Patches/Events/Player/ReservedSlotPatch.cs b/Exiled.Events/Patches/Events/Player/ReservedSlotPatch.cs index 4eeba0e171..79157aa880 100644 --- a/Exiled.Events/Patches/Events/Player/ReservedSlotPatch.cs +++ b/Exiled.Events/Patches/Events/Player/ReservedSlotPatch.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/RevokingMute.cs b/Exiled.Events/Patches/Events/Player/RevokingMute.cs index 1fcf9855e1..5f370f0f13 100644 --- a/Exiled.Events/Patches/Events/Player/RevokingMute.cs +++ b/Exiled.Events/Patches/Events/Player/RevokingMute.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/SearchingPickupEvent.cs b/Exiled.Events/Patches/Events/Player/SearchingPickupEvent.cs index 9973366af4..cb1de74567 100644 --- a/Exiled.Events/Patches/Events/Player/SearchingPickupEvent.cs +++ b/Exiled.Events/Patches/Events/Player/SearchingPickupEvent.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; @@ -34,22 +34,24 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - Label allowLabel = generator.DefineLabel(); + Label retLabel = generator.DefineLabel(); LocalBuilder ev = generator.DeclareLocal(typeof(SearchingPickupEventArgs)); int offset = 1; - int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Stind_Ref) + offset; + int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Brtrue_S) + offset; - // remove base-game check and `SearchSession body` setter - newInstructions.RemoveRange(index, 14); + newInstructions[index].labels.Add(retLabel); + + offset = 1; + index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ret) + offset; newInstructions.InsertRange( index, new[] { // Player.Get(Hub) - new(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), new(OpCodes.Callvirt, PropertyGetter(typeof(SearchCoordinator), nameof(SearchCoordinator.Hub))), new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), @@ -82,27 +84,16 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable i.opcode == OpCodes.Stloc_S && i.operand is LocalBuilder { LocalIndex: 4 }) + offset; + // replace "request.Target.SearchTimeForPlayer(this.Hub);" with ev.SearchTime // remove base-game SearchTime setter newInstructions.RemoveRange(index, 5); @@ -134,4 +126,4 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } } -} \ No newline at end of file +} diff --git a/Exiled.Events/Patches/Events/Player/SendingAdminChatMessage.cs b/Exiled.Events/Patches/Events/Player/SendingAdminChatMessage.cs index d05cf48019..1a1eaf9714 100644 --- a/Exiled.Events/Patches/Events/Player/SendingAdminChatMessage.cs +++ b/Exiled.Events/Patches/Events/Player/SendingAdminChatMessage.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Shooting.cs b/Exiled.Events/Patches/Events/Player/Shooting.cs index cc54f72fa7..e36c96f037 100644 --- a/Exiled.Events/Patches/Events/Player/Shooting.cs +++ b/Exiled.Events/Patches/Events/Player/Shooting.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Shot.cs b/Exiled.Events/Patches/Events/Player/Shot.cs index ff34d42a39..ac684b6325 100644 --- a/Exiled.Events/Patches/Events/Player/Shot.cs +++ b/Exiled.Events/Patches/Events/Player/Shot.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using EventArgs.Player; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Player/ShowingHitMarker.cs b/Exiled.Events/Patches/Events/Player/ShowingHitMarker.cs new file mode 100644 index 0000000000..79945d183d --- /dev/null +++ b/Exiled.Events/Patches/Events/Player/ShowingHitMarker.cs @@ -0,0 +1,86 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Player +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Core.Generic.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Player; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.OnShowingHitMarker))] + [HarmonyPatch(typeof(Hitmarker), nameof(Hitmarker.SendHitmarkerDirectly), typeof(ReferenceHub), typeof(float), typeof(bool))] + internal class ShowingHitMarker + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Ldarg_1); + + Label continueLabel = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(DisplayingHitmarkerEventArgs)); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(hub); + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // size + new(OpCodes.Ldarg_1), + + // play audio + new(OpCodes.Ldarg_2), + + // true + new(OpCodes.Ldc_I4_1), + + // DisplayingHitmarkerEventArgs ev = new(Player, float, bool, true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DisplayingHitmarkerEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Player.OnShowingHitMarker(ev); + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShowingHitMarker))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(DisplayingHitmarkerEventArgs), nameof(DisplayingHitmarkerEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + + new(OpCodes.Ret), + + // size = ev.Size; + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).WithLabels(continueLabel), + new(OpCodes.Callvirt, PropertyGetter(typeof(DisplayingHitmarkerEventArgs), nameof(DisplayingHitmarkerEventArgs.Size))), + new(OpCodes.Starg_S, 1), + + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(DisplayingHitmarkerEventArgs), nameof(DisplayingHitmarkerEventArgs.PlayAudio))), + new(OpCodes.Starg_S, 2), + }); + + 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.Events/Patches/Events/Player/SpawningRagdoll.cs b/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs index 39f5179cef..69409b30f2 100644 --- a/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs +++ b/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; @@ -47,16 +47,17 @@ private static IEnumerable Transpiler(IEnumerable instruction.opcode == OpCodes.Ldloc_1) + offset; - newInstructions.RemoveRange(index, 7); - - index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldloc_1); + // remove + // "basicRagdoll.NetworkInfo = new RagdollData(owner, handler, transform.localPosition, transform.localRotation);" + newInstructions.RemoveRange(index, 9); + // replace with newInstructions.InsertRange(index, new[] { - // hub + // hub new CodeInstruction(OpCodes.Ldarg_0), // handler @@ -73,9 +74,6 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.Calls(PropertySetter(typeof(BasicRagdoll), nameof(BasicRagdoll.NetworkInfo)))) + offset; - - newInstructions.InsertRange(index, new CodeInstruction[] - { - // ev.Info + // basicRagdoll.NetworkInfo = ev.Info + new(OpCodes.Ldloc_1), new(OpCodes.Ldloc_S, ev.LocalIndex), new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningRagdollEventArgs), nameof(SpawningRagdollEventArgs.Info))), + new(OpCodes.Call, PropertySetter(typeof(BasicRagdoll), nameof(BasicRagdoll.NetworkInfo))), // new Vector3() new(OpCodes.Ldloca_S, targetScale.LocalIndex), diff --git a/Exiled.Events/Patches/Events/Player/StayingOnEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/StayingOnEnvironmentalHazard.cs index 93cf67e745..75aed57a16 100644 --- a/Exiled.Events/Patches/Events/Player/StayingOnEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/StayingOnEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/StayingOnSinkholeEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/StayingOnSinkholeEnvironmentalHazard.cs index 5121f9ed2f..35f426088f 100644 --- a/Exiled.Events/Patches/Events/Player/StayingOnSinkholeEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/StayingOnSinkholeEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/StayingOnTantrumEnvironmentalHazard.cs b/Exiled.Events/Patches/Events/Player/StayingOnTantrumEnvironmentalHazard.cs index a4a85368df..5daa893df2 100644 --- a/Exiled.Events/Patches/Events/Player/StayingOnTantrumEnvironmentalHazard.cs +++ b/Exiled.Events/Patches/Events/Player/StayingOnTantrumEnvironmentalHazard.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/ThrowingRequest.cs b/Exiled.Events/Patches/Events/Player/ThrowingRequest.cs index 2d882562fc..018ec41c80 100644 --- a/Exiled.Events/Patches/Events/Player/ThrowingRequest.cs +++ b/Exiled.Events/Patches/Events/Player/ThrowingRequest.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/ThrownProjectile.cs b/Exiled.Events/Patches/Events/Player/ThrownProjectile.cs index bb90b5789f..d54ff06ba6 100644 --- a/Exiled.Events/Patches/Events/Player/ThrownProjectile.cs +++ b/Exiled.Events/Patches/Events/Player/ThrownProjectile.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/TogglingFlashlight.cs b/Exiled.Events/Patches/Events/Player/TogglingFlashlight.cs index becb70fc98..a24d82175c 100644 --- a/Exiled.Events/Patches/Events/Player/TogglingFlashlight.cs +++ b/Exiled.Events/Patches/Events/Player/TogglingFlashlight.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/TogglingNoClip.cs b/Exiled.Events/Patches/Events/Player/TogglingNoClip.cs index 6af54c2c0b..f929cff3e8 100644 --- a/Exiled.Events/Patches/Events/Player/TogglingNoClip.cs +++ b/Exiled.Events/Patches/Events/Player/TogglingNoClip.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features.Roles; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Player/TogglingOverwatch.cs b/Exiled.Events/Patches/Events/Player/TogglingOverwatch.cs index a77e03790b..b1bdf3b8df 100644 --- a/Exiled.Events/Patches/Events/Player/TogglingOverwatch.cs +++ b/Exiled.Events/Patches/Events/Player/TogglingOverwatch.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using CommandSystem.Commands.RemoteAdmin; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/TogglingRadio.cs b/Exiled.Events/Patches/Events/Player/TogglingRadio.cs index 5760dbfadc..e7bf18f921 100644 --- a/Exiled.Events/Patches/Events/Player/TogglingRadio.cs +++ b/Exiled.Events/Patches/Events/Player/TogglingRadio.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/TriggeringTesla.cs b/Exiled.Events/Patches/Events/Player/TriggeringTesla.cs index 16f948a96f..53ddc9afcd 100644 --- a/Exiled.Events/Patches/Events/Player/TriggeringTesla.cs +++ b/Exiled.Events/Patches/Events/Player/TriggeringTesla.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/UsingAndCancellingItemUse.cs b/Exiled.Events/Patches/Events/Player/UsingAndCancellingItemUse.cs index 9004441dc8..97fea96cd0 100644 --- a/Exiled.Events/Patches/Events/Player/UsingAndCancellingItemUse.cs +++ b/Exiled.Events/Patches/Events/Player/UsingAndCancellingItemUse.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/UsingItemCompleted.cs b/Exiled.Events/Patches/Events/Player/UsingItemCompleted.cs index c884e213c3..f60d781aa2 100644 --- a/Exiled.Events/Patches/Events/Player/UsingItemCompleted.cs +++ b/Exiled.Events/Patches/Events/Player/UsingItemCompleted.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/UsingMicroHIDEnergy.cs b/Exiled.Events/Patches/Events/Player/UsingMicroHIDEnergy.cs index c7ca375c0b..45dea2866b 100644 --- a/Exiled.Events/Patches/Events/Player/UsingMicroHIDEnergy.cs +++ b/Exiled.Events/Patches/Events/Player/UsingMicroHIDEnergy.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/UsingRadioBattery.cs b/Exiled.Events/Patches/Events/Player/UsingRadioBattery.cs index b4a52b2ebe..4b4f35ee32 100644 --- a/Exiled.Events/Patches/Events/Player/UsingRadioBattery.cs +++ b/Exiled.Events/Patches/Events/Player/UsingRadioBattery.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Player/Verified.cs b/Exiled.Events/Patches/Events/Player/Verified.cs index 4e08050805..e2f20948d8 100644 --- a/Exiled.Events/Patches/Events/Player/Verified.cs +++ b/Exiled.Events/Patches/Events/Player/Verified.cs @@ -14,7 +14,7 @@ namespace Exiled.Events.Patches.Events.Player using API.Features; using CentralAuth; using Exiled.API.Extensions; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Player/VoiceChatting.cs b/Exiled.Events/Patches/Events/Player/VoiceChatting.cs index 875e099b00..055539d9c5 100644 --- a/Exiled.Events/Patches/Events/Player/VoiceChatting.cs +++ b/Exiled.Events/Patches/Events/Player/VoiceChatting.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using API.Features.Roles; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; diff --git a/Exiled.Events/Patches/Events/Scp049/ActivatingSense.cs b/Exiled.Events/Patches/Events/Scp049/ActivatingSense.cs index 1dced2fa34..f5c799da63 100644 --- a/Exiled.Events/Patches/Events/Scp049/ActivatingSense.cs +++ b/Exiled.Events/Patches/Events/Scp049/ActivatingSense.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp049 using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Scp049; using HarmonyLib; using PlayerRoles.PlayableScps.Scp049; diff --git a/Exiled.Events/Patches/Events/Scp049/Attacking.cs b/Exiled.Events/Patches/Events/Scp049/Attacking.cs index 9e822c8af2..b57d465b2e 100644 --- a/Exiled.Events/Patches/Events/Scp049/Attacking.cs +++ b/Exiled.Events/Patches/Events/Scp049/Attacking.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp049 using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp049; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs b/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs index 2eab40acc5..6fad6c6db0 100644 --- a/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs +++ b/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp049 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp049; @@ -25,8 +25,11 @@ 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.Events/Patches/Events/Scp049/SendingCall.cs b/Exiled.Events/Patches/Events/Scp049/SendingCall.cs index dac154409f..fed6c607b8 100644 --- a/Exiled.Events/Patches/Events/Scp049/SendingCall.cs +++ b/Exiled.Events/Patches/Events/Scp049/SendingCall.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp049 using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Scp049; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp049/StartingRecall.cs b/Exiled.Events/Patches/Events/Scp049/StartingRecall.cs index 99d1fad8f4..05e44699fb 100644 --- a/Exiled.Events/Patches/Events/Scp049/StartingRecall.cs +++ b/Exiled.Events/Patches/Events/Scp049/StartingRecall.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp049 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp049; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp0492/Consumed.cs b/Exiled.Events/Patches/Events/Scp0492/Consumed.cs index 3eb7ddb7c6..69655c45f6 100644 --- a/Exiled.Events/Patches/Events/Scp0492/Consumed.cs +++ b/Exiled.Events/Patches/Events/Scp0492/Consumed.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp0492 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp0492; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp0492/Consuming.cs b/Exiled.Events/Patches/Events/Scp0492/Consuming.cs index 1216208f92..fcc91617a3 100644 --- a/Exiled.Events/Patches/Events/Scp0492/Consuming.cs +++ b/Exiled.Events/Patches/Events/Scp0492/Consuming.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp0492 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp0492; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp0492/TriggeringBloodlustEvent.cs b/Exiled.Events/Patches/Events/Scp0492/TriggeringBloodlustEvent.cs index 596ae1a64a..3ea31ddf6d 100644 --- a/Exiled.Events/Patches/Events/Scp0492/TriggeringBloodlustEvent.cs +++ b/Exiled.Events/Patches/Events/Scp0492/TriggeringBloodlustEvent.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp0492 using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp0492; diff --git a/Exiled.Events/Patches/Events/Scp079/ChangingCamera.cs b/Exiled.Events/Patches/Events/Scp079/ChangingCamera.cs index a1c83f6e04..56f41bcf2e 100644 --- a/Exiled.Events/Patches/Events/Scp079/ChangingCamera.cs +++ b/Exiled.Events/Patches/Events/Scp079/ChangingCamera.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/ChangingSpeakerStatusAndVoiceChatting.cs b/Exiled.Events/Patches/Events/Scp079/ChangingSpeakerStatusAndVoiceChatting.cs index 15223b800c..0e229dc725 100644 --- a/Exiled.Events/Patches/Events/Scp079/ChangingSpeakerStatusAndVoiceChatting.cs +++ b/Exiled.Events/Patches/Events/Scp079/ChangingSpeakerStatusAndVoiceChatting.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/ElevatorTeleporting.cs b/Exiled.Events/Patches/Events/Scp079/ElevatorTeleporting.cs index d461dbed9a..d2dd6cb36d 100644 --- a/Exiled.Events/Patches/Events/Scp079/ElevatorTeleporting.cs +++ b/Exiled.Events/Patches/Events/Scp079/ElevatorTeleporting.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/GainingExperience.cs b/Exiled.Events/Patches/Events/Scp079/GainingExperience.cs index fd212ee6fe..076be28659 100644 --- a/Exiled.Events/Patches/Events/Scp079/GainingExperience.cs +++ b/Exiled.Events/Patches/Events/Scp079/GainingExperience.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/GainingLevel.cs b/Exiled.Events/Patches/Events/Scp079/GainingLevel.cs index 7721bc0035..1385eb3f25 100644 --- a/Exiled.Events/Patches/Events/Scp079/GainingLevel.cs +++ b/Exiled.Events/Patches/Events/Scp079/GainingLevel.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/InteractingTesla.cs b/Exiled.Events/Patches/Events/Scp079/InteractingTesla.cs index bc44248311..6a0c56e6e1 100644 --- a/Exiled.Events/Patches/Events/Scp079/InteractingTesla.cs +++ b/Exiled.Events/Patches/Events/Scp079/InteractingTesla.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/LockingDown.cs b/Exiled.Events/Patches/Events/Scp079/LockingDown.cs index ad5c1d86ed..f4c6b8ebc7 100644 --- a/Exiled.Events/Patches/Events/Scp079/LockingDown.cs +++ b/Exiled.Events/Patches/Events/Scp079/LockingDown.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp079/Pinging.cs b/Exiled.Events/Patches/Events/Scp079/Pinging.cs index fb8bed7509..59382aa462 100644 --- a/Exiled.Events/Patches/Events/Scp079/Pinging.cs +++ b/Exiled.Events/Patches/Events/Scp079/Pinging.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp079/RoomBlackout.cs b/Exiled.Events/Patches/Events/Scp079/RoomBlackout.cs index 615110735a..ad645139f0 100644 --- a/Exiled.Events/Patches/Events/Scp079/RoomBlackout.cs +++ b/Exiled.Events/Patches/Events/Scp079/RoomBlackout.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp079/TriggeringDoor.cs b/Exiled.Events/Patches/Events/Scp079/TriggeringDoor.cs index aa9beb8249..b21f02d8f8 100644 --- a/Exiled.Events/Patches/Events/Scp079/TriggeringDoor.cs +++ b/Exiled.Events/Patches/Events/Scp079/TriggeringDoor.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features.Doors; using Exiled.Events.Attributes; diff --git a/Exiled.Events/Patches/Events/Scp079/ZoneBlackout.cs b/Exiled.Events/Patches/Events/Scp079/ZoneBlackout.cs index 5cc8cbd10d..bfcf5934e0 100644 --- a/Exiled.Events/Patches/Events/Scp079/ZoneBlackout.cs +++ b/Exiled.Events/Patches/Events/Scp079/ZoneBlackout.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp079 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Extensions; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp079; diff --git a/Exiled.Events/Patches/Events/Scp096/AddingTarget.cs b/Exiled.Events/Patches/Events/Scp096/AddingTarget.cs index 408de36295..77b74ef6d1 100644 --- a/Exiled.Events/Patches/Events/Scp096/AddingTarget.cs +++ b/Exiled.Events/Patches/Events/Scp096/AddingTarget.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp096 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp096; diff --git a/Exiled.Events/Patches/Events/Scp096/CalmingDown.cs b/Exiled.Events/Patches/Events/Scp096/CalmingDown.cs index 484190a49e..202ff64e95 100644 --- a/Exiled.Events/Patches/Events/Scp096/CalmingDown.cs +++ b/Exiled.Events/Patches/Events/Scp096/CalmingDown.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp096 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp096; diff --git a/Exiled.Events/Patches/Events/Scp096/Charging.cs b/Exiled.Events/Patches/Events/Scp096/Charging.cs index 85e799977c..aa515abfa8 100644 --- a/Exiled.Events/Patches/Events/Scp096/Charging.cs +++ b/Exiled.Events/Patches/Events/Scp096/Charging.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp096 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp096; diff --git a/Exiled.Events/Patches/Events/Scp096/Enraging.cs b/Exiled.Events/Patches/Events/Scp096/Enraging.cs index 5e0154629d..457545edf9 100644 --- a/Exiled.Events/Patches/Events/Scp096/Enraging.cs +++ b/Exiled.Events/Patches/Events/Scp096/Enraging.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp096 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp096; diff --git a/Exiled.Events/Patches/Events/Scp096/StartPryingGate.cs b/Exiled.Events/Patches/Events/Scp096/StartPryingGate.cs index 420ef8c44f..304a712547 100644 --- a/Exiled.Events/Patches/Events/Scp096/StartPryingGate.cs +++ b/Exiled.Events/Patches/Events/Scp096/StartPryingGate.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp096 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp096; diff --git a/Exiled.Events/Patches/Events/Scp096/TryingNotToCry.cs b/Exiled.Events/Patches/Events/Scp096/TryingNotToCry.cs index 63c40350fe..0cca7abcdf 100644 --- a/Exiled.Events/Patches/Events/Scp096/TryingNotToCry.cs +++ b/Exiled.Events/Patches/Events/Scp096/TryingNotToCry.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp096 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp096; diff --git a/Exiled.Events/Patches/Events/Scp106/Attacking.cs b/Exiled.Events/Patches/Events/Scp106/Attacking.cs index 244d6447e4..6c02409a2a 100644 --- a/Exiled.Events/Patches/Events/Scp106/Attacking.cs +++ b/Exiled.Events/Patches/Events/Scp106/Attacking.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Scp106 using System.Reflection.Emit; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp106; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp106/ExitStalking.cs b/Exiled.Events/Patches/Events/Scp106/ExitStalking.cs index ac93ec10f4..85f9c4e4ec 100644 --- a/Exiled.Events/Patches/Events/Scp106/ExitStalking.cs +++ b/Exiled.Events/Patches/Events/Scp106/ExitStalking.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp106 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp106; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp106/Stalking.cs b/Exiled.Events/Patches/Events/Scp106/Stalking.cs index a59523f7fa..9098fd65e3 100644 --- a/Exiled.Events/Patches/Events/Scp106/Stalking.cs +++ b/Exiled.Events/Patches/Events/Scp106/Stalking.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp106 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp106; using HarmonyLib; diff --git a/Exiled.Events/Patches/Events/Scp106/Teleporting.cs b/Exiled.Events/Patches/Events/Scp106/Teleporting.cs index f5d4aa9a39..44f18c04d4 100644 --- a/Exiled.Events/Patches/Events/Scp106/Teleporting.cs +++ b/Exiled.Events/Patches/Events/Scp106/Teleporting.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp106 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp106; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp173/Blinking.cs b/Exiled.Events/Patches/Events/Scp173/Blinking.cs index bb2f1ab61a..c0589fa48f 100644 --- a/Exiled.Events/Patches/Events/Scp173/Blinking.cs +++ b/Exiled.Events/Patches/Events/Scp173/Blinking.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Events.Scp173 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp173; diff --git a/Exiled.Events/Patches/Events/Scp173/BlinkingRequest.cs b/Exiled.Events/Patches/Events/Scp173/BlinkingRequest.cs index 0671b9e0ea..674b794e4d 100644 --- a/Exiled.Events/Patches/Events/Scp173/BlinkingRequest.cs +++ b/Exiled.Events/Patches/Events/Scp173/BlinkingRequest.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp173 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp173; diff --git a/Exiled.Events/Patches/Events/Scp173/Observers.cs b/Exiled.Events/Patches/Events/Scp173/Observers.cs new file mode 100644 index 0000000000..c32b121a6a --- /dev/null +++ b/Exiled.Events/Patches/Events/Scp173/Observers.cs @@ -0,0 +1,116 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp173 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features; + using Attributes; + using Exiled.API.Features.Core.Generic.Pools; + using Exiled.Events.EventArgs.Scp173; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp173; + using PlayerRoles.Subroutines; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Scp173), nameof(Handlers.Scp173.AddingObserver))] + [HarmonyPatch(typeof(Scp173ObserversTracker), nameof(Scp173ObserversTracker.UpdateObserver))] + internal static class Observers + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + // AddingObserver patch + Label returnLabel = generator.DefineLabel(); + LocalBuilder ev = generator.DeclareLocal(typeof(AddingObserverEventArgs)); + + int index = newInstructions.FindIndex(x => x.Calls(Method(typeof(HashSet), nameof(HashSet.Add)))) + 2; + newInstructions[index].labels.Add(returnLabel); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(Owner); + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // Player.Get(ply); + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // AddingObserverEventArgs ev = new(Player.Get(Owner), Player.Get(ply), true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AddingObserverEventArgs))[0]), + new(OpCodes.Stloc, ev), + + // Scp173.OnAddingObserver(ev); + new(OpCodes.Ldloc, ev), + new(OpCodes.Call, Method(typeof(Handlers.Scp173), nameof(Handlers.Scp173.OnAddingObserver))), + + // if (!ev.IsAllowed) + new(OpCodes.Ldloc, ev), + new(OpCodes.Callvirt, + PropertyGetter(typeof(AddingObserverEventArgs), nameof(AddingObserverEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, returnLabel), + + // Observers.Remove(ply); + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(Scp173ObserversTracker), nameof(Scp173ObserversTracker.Observers))), + new(OpCodes.Ldarg_1), + new(OpCodes.Callvirt, Method(typeof(HashSet), nameof(HashSet.Remove))), + new(OpCodes.Pop), + + // return 0; + new(OpCodes.Ldc_I4_0), + new(OpCodes.Ret), + }); + + // RemoveObserver patch + int index2 = newInstructions.FindLastIndex(x => x.Calls(Method(typeof(HashSet), nameof(HashSet.Remove)))) + 2; + + LocalBuilder ev2 = generator.DeclareLocal(typeof(RemoveObserverEventArgs)); + + newInstructions.InsertRange(index2, new CodeInstruction[] + { + // Player.Get(Owner); + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, + PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // Player.Get(ply); + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // new RemoveObserverEventArgs(Player.Get(Owner), Player.Get(ply)); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemoveObserverEventArgs))[0]), + new(OpCodes.Stloc, ev2), + + // Scp173.OnRemovingObserver(new RemoveObserverEventArgs(Player.Get(Owner), Player.Get(ply))); + new(OpCodes.Ldloc, ev2), + new(OpCodes.Call, Method(typeof(Handlers.Scp173), nameof(Handlers.Scp173.OnRemoveObserver))), + }); + + 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.Events/Patches/Events/Scp173/PlacingTantrum.cs b/Exiled.Events/Patches/Events/Scp173/PlacingTantrum.cs index 91db1826cc..122235dce5 100644 --- a/Exiled.Events/Patches/Events/Scp173/PlacingTantrum.cs +++ b/Exiled.Events/Patches/Events/Scp173/PlacingTantrum.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp173 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp173; diff --git a/Exiled.Events/Patches/Events/Scp173/UsingBreakneckSpeeds.cs b/Exiled.Events/Patches/Events/Scp173/UsingBreakneckSpeeds.cs index 8acf3f6f94..b914670e42 100644 --- a/Exiled.Events/Patches/Events/Scp173/UsingBreakneckSpeeds.cs +++ b/Exiled.Events/Patches/Events/Scp173/UsingBreakneckSpeeds.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp173 using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp173; diff --git a/Exiled.Events/Patches/Events/Scp244/DamagingScp244.cs b/Exiled.Events/Patches/Events/Scp244/DamagingScp244.cs index 7bb4a51603..22426c5d1a 100644 --- a/Exiled.Events/Patches/Events/Scp244/DamagingScp244.cs +++ b/Exiled.Events/Patches/Events/Scp244/DamagingScp244.cs @@ -10,8 +10,8 @@ namespace Exiled.Events.Patches.Events.Scp244 using System.Collections.Generic; using System.Reflection.Emit; + using API.Features.Core.Generic.Pools; using API.Features.DamageHandlers; - using API.Features.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp244; diff --git a/Exiled.Events/Patches/Events/Scp244/UpdateScp244.cs b/Exiled.Events/Patches/Events/Scp244/UpdateScp244.cs index 2443644675..832fb0f6b0 100644 --- a/Exiled.Events/Patches/Events/Scp244/UpdateScp244.cs +++ b/Exiled.Events/Patches/Events/Scp244/UpdateScp244.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp244 using System.Diagnostics; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp244; diff --git a/Exiled.Events/Patches/Events/Scp244/UsingScp244.cs b/Exiled.Events/Patches/Events/Scp244/UsingScp244.cs index 586d1d4302..a1ed1fa8f9 100644 --- a/Exiled.Events/Patches/Events/Scp244/UsingScp244.cs +++ b/Exiled.Events/Patches/Events/Scp244/UsingScp244.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp244 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp244; diff --git a/Exiled.Events/Patches/Events/Scp3114/Revealing.cs b/Exiled.Events/Patches/Events/Scp3114/Revealing.cs index ca8d6988a8..47804ba0e0 100644 --- a/Exiled.Events/Patches/Events/Scp3114/Revealing.cs +++ b/Exiled.Events/Patches/Events/Scp3114/Revealing.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Scp3114 using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp3114; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp3114/Strangling.cs b/Exiled.Events/Patches/Events/Scp3114/Strangling.cs index 459c0b0a0d..eeec25b659 100644 --- a/Exiled.Events/Patches/Events/Scp3114/Strangling.cs +++ b/Exiled.Events/Patches/Events/Scp3114/Strangling.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp3114 using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp3114; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp3114/TryUseBody.cs b/Exiled.Events/Patches/Events/Scp3114/TryUseBody.cs index d3b9bc87a7..fb6106e2e5 100644 --- a/Exiled.Events/Patches/Events/Scp3114/TryUseBody.cs +++ b/Exiled.Events/Patches/Events/Scp3114/TryUseBody.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp3114 using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp3114; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp3114/VoiceLines.cs b/Exiled.Events/Patches/Events/Scp3114/VoiceLines.cs index 59983bb993..dac4b785c6 100644 --- a/Exiled.Events/Patches/Events/Scp3114/VoiceLines.cs +++ b/Exiled.Events/Patches/Events/Scp3114/VoiceLines.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp3114 using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp3114; using Exiled.Events.Handlers; diff --git a/Exiled.Events/Patches/Events/Scp330/DroppingCandy.cs b/Exiled.Events/Patches/Events/Scp330/DroppingCandy.cs index 5d61a10acd..072c669482 100644 --- a/Exiled.Events/Patches/Events/Scp330/DroppingCandy.cs +++ b/Exiled.Events/Patches/Events/Scp330/DroppingCandy.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp330 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp330; diff --git a/Exiled.Events/Patches/Events/Scp330/EatingScp330.cs b/Exiled.Events/Patches/Events/Scp330/EatingScp330.cs index 1ad59414d4..1c9df069a3 100644 --- a/Exiled.Events/Patches/Events/Scp330/EatingScp330.cs +++ b/Exiled.Events/Patches/Events/Scp330/EatingScp330.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp330 using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Scp330; @@ -42,6 +42,8 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Server +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Core.Generic.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Server; + using HarmonyLib; + using Respawning; + + using static HarmonyLib.AccessTools; + + /// + /// Patches to add + /// , and events. + /// + [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.PreAssigningHumanRoles))] + [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.AssigningHumanRoles))] + [HarmonyPatch(typeof(PlayerRoles.RoleAssign.HumanSpawner), nameof(PlayerRoles.RoleAssign.HumanSpawner.SpawnHumans))] + internal static class HumanSpawner + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder ev = generator.DeclareLocal(typeof(PreAssigningHumanRolesEventArgs)); + + int offset = 1; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Stloc_1) + offset; + + newInstructions.InsertRange(0, new CodeInstruction[] + { + new(OpCodes.Ldarg_0), + new(OpCodes.Ldarg_1), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PreAssigningHumanRolesEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnPreAssigningHumanRoles))), + new(OpCodes.Stloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(PreAssigningHumanRolesEventArgs), nameof(PreAssigningHumanRolesEventArgs.Queue))), + new(OpCodes.Starg_S, 0), + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(PreAssigningHumanRolesEventArgs), nameof(PreAssigningHumanRolesEventArgs.QueueLength))), + new(OpCodes.Starg_S, 0), + }); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + new(OpCodes.Ldloc_1), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AssigningHumanRolesEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnAssigningHumanRoles))), + new(OpCodes.Callvirt, PropertyGetter(typeof(AssigningHumanRolesEventArgs), nameof(AssigningHumanRolesEventArgs.Roles))), + new(OpCodes.Stloc_1), + }); + + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Call); + newInstructions.RemoveAt(index); + + offset = 1; + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldelem_I1) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DeployingHumanRoleEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnDeployingHumanRole))), + new(OpCodes.Callvirt, PropertyGetter(typeof(DeployingHumanRoleEventArgs), nameof(DeployingHumanRoleEventArgs.Delegate))), + new(OpCodes.Callvirt, Method(typeof(Action), nameof(Action.Invoke))), + }); + + 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.Events/Patches/Events/Server/Reporting.cs b/Exiled.Events/Patches/Events/Server/Reporting.cs index bba54c92ed..f7fd265e7b 100644 --- a/Exiled.Events/Patches/Events/Server/Reporting.cs +++ b/Exiled.Events/Patches/Events/Server/Reporting.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Server using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using Exiled.Events.EventArgs.Server; diff --git a/Exiled.Events/Patches/Events/Server/RespawningTeam.cs b/Exiled.Events/Patches/Events/Server/RespawningTeam.cs index a81b42a1eb..eaf7d06a87 100644 --- a/Exiled.Events/Patches/Events/Server/RespawningTeam.cs +++ b/Exiled.Events/Patches/Events/Server/RespawningTeam.cs @@ -7,12 +7,13 @@ namespace Exiled.Events.Patches.Events.Server { + using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Server; using Exiled.Events.Handlers; @@ -25,10 +26,11 @@ namespace Exiled.Events.Patches.Events.Server using Player = API.Features.Player; /// - /// Patch the . - /// Adds the event. + /// Patch the . + /// Adds and events. /// [EventPatch(typeof(Server), nameof(Server.RespawningTeam))] + [EventPatch(typeof(Server), nameof(Server.DeployingTeamRole))] [HarmonyPatch(typeof(RespawnManager), nameof(RespawnManager.Spawn))] internal static class RespawningTeam { @@ -36,79 +38,134 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - int offset = -6; - int index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(UnitNamingRule), nameof(UnitNamingRule.TryGetNamingRule)))) + offset; - - LocalBuilder ev = generator.DeclareLocal(typeof(RespawningTeamEventArgs)); + LocalBuilder preRespawningEv = generator.DeclareLocal(typeof(PreRespawningTeamEventArgs)); + LocalBuilder respawningEv = generator.DeclareLocal(typeof(RespawningTeamEventArgs)); + LocalBuilder deployingEv = generator.DeclareLocal(typeof(DeployingTeamRoleEventArgs)); Label continueLabel = generator.DefineLabel(); + Label ret = generator.DefineLabel(); + Label jne = generator.DefineLabel(); + + int offset = -7; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Sub) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // preRespawningEv = new PreRespawningTeamEventArgs(SpawnableTeamHandlerBase, int, SpawnableTeamType, bool) + // Handlers.Server.OnPreRespawningTeam(preRespawningEv) + new(OpCodes.Ldloc_0), + new(OpCodes.Ldloc_2), + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(RespawnManager), nameof(RespawnManager.NextKnownTeam))), + new(OpCodes.Ldc_I4_1), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PreRespawningTeamEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, preRespawningEv.LocalIndex), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnPreRespawningTeam))), + + // if (!preRespawningEv.IsAllowed) + // goto ret + new(OpCodes.Callvirt, PropertyGetter(typeof(PreRespawningTeamEventArgs), nameof(PreRespawningTeamEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, ret), + + // SpawnableTeamHandlerBase = preRespawningEv.SpawnableTeamHandler + new(OpCodes.Ldloc_S, preRespawningEv.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(PreRespawningTeamEventArgs), nameof(PreRespawningTeamEventArgs.SpawnableTeamHandler))), + new(OpCodes.Stloc_0), + + // this.NextKnownTeam = preRespawningEv.NextKnownTeam + new(OpCodes.Ldarg_0), + new(OpCodes.Ldloc_S, preRespawningEv.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(PreRespawningTeamEventArgs), nameof(PreRespawningTeamEventArgs.NextKnownTeam))), + new(OpCodes.Stfld, Field(typeof(RespawnManager), nameof(RespawnManager.NextKnownTeam))), + + // maxWaveSize = preRespawningEv.MaxWaveSize + new(OpCodes.Ldloc_S, preRespawningEv.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(PreRespawningTeamEventArgs), nameof(PreRespawningTeamEventArgs.MaxWaveSize))), + new(OpCodes.Stloc_2), + }); + + offset = -6; + index = newInstructions.FindIndex(instruction => instruction.Calls(Method(typeof(UnitNamingRule), nameof(UnitNamingRule.TryGetNamingRule)))) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // GetPlayers(list); + new CodeInstruction(OpCodes.Ldloc_1).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(RespawningTeam), nameof(GetPlayers))), + + // maxWaveSize + new(OpCodes.Ldloc_2), + + // this.NextKnownTeam + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(RespawnManager), nameof(RespawnManager.NextKnownTeam))), + + // true + new(OpCodes.Ldc_I4_1), - newInstructions.InsertRange( - index, - new[] - { - // GetPlayers(list); - new CodeInstruction(OpCodes.Ldloc_1).MoveLabelsFrom(newInstructions[index]), - new(OpCodes.Call, Method(typeof(RespawningTeam), nameof(GetPlayers))), - - // maxWaveSize - new(OpCodes.Ldloc_2), - - // this.NextKnownTeam - new(OpCodes.Ldarg_0), - new(OpCodes.Ldfld, Field(typeof(RespawnManager), nameof(RespawnManager.NextKnownTeam))), - - // true - new(OpCodes.Ldc_I4_1), - - // RespawningTeamEventArgs ev = new(players, num, this.NextKnownTeam) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RespawningTeamEventArgs))[0]), - new(OpCodes.Dup), - new(OpCodes.Dup), - new(OpCodes.Stloc, ev.LocalIndex), - - // Handlers.Server.OnRespawningTeam(ev) - new(OpCodes.Call, Method(typeof(Server), nameof(Server.OnRespawningTeam))), - - // if (ev.IsAllowed) - // goto continueLabel; - new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.IsAllowed))), - new(OpCodes.Brtrue_S, continueLabel), - - // this.NextKnownTeam = SpawnableTeam.None - // return; - new(OpCodes.Ldarg_0), - new(OpCodes.Ldc_I4_0), - new(OpCodes.Stfld, Field(typeof(RespawnManager), nameof(RespawnManager.NextKnownTeam))), - new(OpCodes.Ret), - - // load "ev" four times - new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).WithLabels(continueLabel), - new(OpCodes.Dup), - new(OpCodes.Dup), - new(OpCodes.Dup), - - // num = ev.MaximumRespawnAmount - new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.MaximumRespawnAmount))), - new(OpCodes.Stloc_2), - - // spawnableTeamHandler = ev.SpawnableTeam - new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.SpawnableTeam))), - new(OpCodes.Stloc_0), - - // list = GetHubs(ev.Players) - new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.Players))), - new(OpCodes.Call, Method(typeof(RespawningTeam), nameof(GetHubs))), - new(OpCodes.Stloc_1), - - // queueToFill = ev.SpawnQueue; - new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.SpawnQueue))), - new(OpCodes.Stloc, 7), - }); + // RespawningTeamEventArgs respawningEv = new(players, num, this.NextKnownTeam) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RespawningTeamEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc, respawningEv.LocalIndex), + + // Handlers.Server.OnRespawningTeam(respawningEv) + new(OpCodes.Call, Method(typeof(Server), nameof(Server.OnRespawningTeam))), + + // if (respawningEv.IsAllowed) + // goto continueLabel; + new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + + new CodeInstruction(OpCodes.Ldloc_S, respawningEv.LocalIndex).WithLabels(continueLabel), + new(OpCodes.Dup), + + // list = GetHubs(ev.Players) + new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.Players))), + new(OpCodes.Call, Method(typeof(RespawningTeam), nameof(GetHubs))), + new(OpCodes.Stloc_1), + + // queueToFill = ev.SpawnQueue; + new(OpCodes.Callvirt, PropertyGetter(typeof(RespawningTeamEventArgs), nameof(RespawningTeamEventArgs.SpawnQueue))), + new(OpCodes.Stloc, 7), + }); offset = -6; newInstructions.RemoveRange(newInstructions.FindIndex(i => i.opcode == OpCodes.Callvirt && (MethodInfo)i.operand == Method(typeof(SpawnableTeamHandlerBase), nameof(SpawnableTeamHandlerBase.GenerateQueue))) + offset, 7); + offset = -3; + index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldc_I4_M1) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + new(OpCodes.Ldloc_S, 10), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DeployingTeamRoleEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnDeployingTeamRole))), + new(OpCodes.Stloc_S, deployingEv.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(DeployingTeamRoleEventArgs), nameof(DeployingTeamRoleEventArgs.Role))), + new(OpCodes.Ldloc_S, deployingEv.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(DeployingTeamRoleEventArgs), nameof(DeployingTeamRoleEventArgs.Delegate))), + new(OpCodes.Callvirt, Method(typeof(Action), nameof(Action.Invoke))), + new(OpCodes.Ldloc_S, deployingEv.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(DeployingTeamRoleEventArgs), nameof(DeployingTeamRoleEventArgs.IsReliable))), + new(OpCodes.Brfalse_S, jne), + }); + + offset = 5; + index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldc_I4_M1) + offset; + newInstructions[index].labels.Add(jne); + + offset = -3; + index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldc_I4_M1) + offset; + + newInstructions.RemoveRange(index, 5); + + newInstructions[newInstructions.Count - 1].labels.Add(ret); + for (int z = 0; z < newInstructions.Count; z++) yield return newInstructions[z]; diff --git a/Exiled.Events/Patches/Events/Server/RestartedRespawnSequence.cs b/Exiled.Events/Patches/Events/Server/RestartedRespawnSequence.cs new file mode 100644 index 0000000000..d23e3096c4 --- /dev/null +++ b/Exiled.Events/Patches/Events/Server/RestartedRespawnSequence.cs @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Server +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Reflection.Emit; + + using API.Features.Core.Generic.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Server; + using Exiled.Events.Handlers; + using HarmonyLib; + using Respawning; + using Respawning.NamingRules; + + using static HarmonyLib.AccessTools; + + using Player = API.Features.Player; + + /// + /// Patch the . + /// Adds the event. + /// + [EventPatch(typeof(Server), nameof(Server.RestartedRespawnSequence))] + [HarmonyPatch(typeof(RespawnManager), nameof(RespawnManager.RestartSequence))] + internal static class RestartedRespawnSequence + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + CodeInstruction[] toInsert = new CodeInstruction[] + { + new(OpCodes.Ldarg_0), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RestartedRespawnSequenceEventArgs))[0]), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnRestartedRespawnSequence))), + }; + + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ret); + newInstructions.InsertRange(index, toInsert); + + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ret); + newInstructions.InsertRange(index, toInsert); + + 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.Events/Patches/Events/Server/RestartingRound.cs b/Exiled.Events/Patches/Events/Server/RestartingRound.cs index 99c3a5e06c..341bd19eaf 100644 --- a/Exiled.Events/Patches/Events/Server/RestartingRound.cs +++ b/Exiled.Events/Patches/Events/Server/RestartingRound.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Server using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using GameCore; @@ -42,7 +42,8 @@ private static IEnumerable Transpiler(IEnumerable +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Server +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Core.Generic.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Server; + + using HarmonyLib; + + using Respawning; + + using static HarmonyLib.AccessTools; + + /// + /// Patches to add + /// , and events. + /// + [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.PreAssigningScpRoles))] + [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.AssigningScpRoles))] + [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.DeployingScpRole))] + [HarmonyPatch(typeof(PlayerRoles.RoleAssign.ScpSpawner), nameof(PlayerRoles.RoleAssign.ScpSpawner.SpawnScps))] + internal static class ScpSpawner + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder ev = generator.DeclareLocal(typeof(AssigningScpRolesEventArgs)); + + int offset = 1; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Stloc_0) + offset; + + newInstructions.InsertRange(0, new CodeInstruction[] + { + new(OpCodes.Ldarg_0), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PreAssigningScpRolesEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnPreAssigningScpRoles))), + new(OpCodes.Callvirt, PropertyGetter(typeof(PreAssigningScpRolesEventArgs), nameof(PreAssigningScpRolesEventArgs.Amount))), + new(OpCodes.Starg_S, 0), + }); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + new(OpCodes.Ldloc_0), + new(OpCodes.Ldsfld, Field(typeof(PlayerRoles.RoleAssign.ScpSpawner), nameof(PlayerRoles.RoleAssign.ScpSpawner.EnqueuedScps))), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AssigningScpRolesEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnAssigningScpRoles))), + new(OpCodes.Stloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(AssigningScpRolesEventArgs), nameof(AssigningScpRolesEventArgs.ChosenPlayers))), + new(OpCodes.Stloc_0), + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(AssigningScpRolesEventArgs), nameof(AssigningScpRolesEventArgs.Roles))), + new(OpCodes.Stsfld, Field(typeof(PlayerRoles.RoleAssign.ScpSpawner), nameof(PlayerRoles.RoleAssign.ScpSpawner.EnqueuedScps))), + }); + + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldloc_0); + newInstructions.RemoveRange(index, 4); + + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldsfld); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + new(OpCodes.Ldloc_0), + new(OpCodes.Ldloc_2), + new(OpCodes.Ldsfld, Field(typeof(PlayerRoles.RoleAssign.ScpSpawner), nameof(PlayerRoles.RoleAssign.ScpSpawner.EnqueuedScps))), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DeployingScpRoleEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnDeployingScpRole))), + new(OpCodes.Callvirt, PropertyGetter(typeof(DeployingScpRoleEventArgs), nameof(DeployingScpRoleEventArgs.Delegate))), + new(OpCodes.Callvirt, Method(typeof(Action), nameof(Action.Invoke))), + }); + + 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.Events/Patches/Events/Server/SelectingRespawnTeam.cs b/Exiled.Events/Patches/Events/Server/SelectingRespawnTeam.cs index 3418332acc..8272e10c1a 100644 --- a/Exiled.Events/Patches/Events/Server/SelectingRespawnTeam.cs +++ b/Exiled.Events/Patches/Events/Server/SelectingRespawnTeam.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Server using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Server; diff --git a/Exiled.Events/Patches/Events/Warhead/ChangingLeverStatus.cs b/Exiled.Events/Patches/Events/Warhead/ChangingLeverStatus.cs index b03e488692..ba3020ac9b 100644 --- a/Exiled.Events/Patches/Events/Warhead/ChangingLeverStatus.cs +++ b/Exiled.Events/Patches/Events/Warhead/ChangingLeverStatus.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Warhead using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Warhead; diff --git a/Exiled.Events/Patches/Events/Warhead/Detonation.cs b/Exiled.Events/Patches/Events/Warhead/Detonation.cs index 016d530872..1b39f95456 100644 --- a/Exiled.Events/Patches/Events/Warhead/Detonation.cs +++ b/Exiled.Events/Patches/Events/Warhead/Detonation.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Events.Warhead using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Warhead; using Handlers; diff --git a/Exiled.Events/Patches/Events/Warhead/Starting.cs b/Exiled.Events/Patches/Events/Warhead/Starting.cs index 42563ae54a..ebffe08f37 100644 --- a/Exiled.Events/Patches/Events/Warhead/Starting.cs +++ b/Exiled.Events/Patches/Events/Warhead/Starting.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Warhead using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Warhead; diff --git a/Exiled.Events/Patches/Events/Warhead/Stopping.cs b/Exiled.Events/Patches/Events/Warhead/Stopping.cs index a9efe02cb0..e9ca2cba44 100644 --- a/Exiled.Events/Patches/Events/Warhead/Stopping.cs +++ b/Exiled.Events/Patches/Events/Warhead/Stopping.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Warhead using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Warhead; diff --git a/Exiled.Events/Patches/Fixes/FixPickupPreviousOwner.cs b/Exiled.Events/Patches/Fixes/FixPickupPreviousOwner.cs index 22f5a2db78..4e6ceecb96 100644 --- a/Exiled.Events/Patches/Fixes/FixPickupPreviousOwner.cs +++ b/Exiled.Events/Patches/Fixes/FixPickupPreviousOwner.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Footprinting; using HarmonyLib; using InventorySystem; diff --git a/Exiled.Events/Patches/Fixes/GetAmmoLimitFix.cs b/Exiled.Events/Patches/Fixes/GetAmmoLimitFix.cs index 54465f361d..6d18cadd4a 100644 --- a/Exiled.Events/Patches/Fixes/GetAmmoLimitFix.cs +++ b/Exiled.Events/Patches/Fixes/GetAmmoLimitFix.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; using InventorySystem.Configs; diff --git a/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs b/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs index bb42e350a2..f6949c7400 100644 --- a/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs +++ b/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs @@ -10,9 +10,9 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; + using API.Features.Core.Generic.Pools; using API.Features.Items; using API.Features.Pickups.Projectiles; - using API.Features.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Fixes/HurtingFix.cs b/Exiled.Events/Patches/Fixes/HurtingFix.cs index 56f5f2cf09..31c22bdbe8 100644 --- a/Exiled.Events/Patches/Fixes/HurtingFix.cs +++ b/Exiled.Events/Patches/Fixes/HurtingFix.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Events.Player using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Fixes/LockerFixes.cs b/Exiled.Events/Patches/Fixes/LockerFixes.cs index ca5577a46c..1dc9dc1f35 100644 --- a/Exiled.Events/Patches/Fixes/LockerFixes.cs +++ b/Exiled.Events/Patches/Fixes/LockerFixes.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using HarmonyLib; @@ -36,7 +36,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } - private static void Hepler(LockerChamber chamber) + private static void Helper(LockerChamber chamber) { chamber._content.Clear(); diff --git a/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.cs b/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.cs new file mode 100644 index 0000000000..366a531d74 --- /dev/null +++ b/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.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 API.Features.Core.Generic.Pools; + using HarmonyLib; + using Interactables.Interobjects; + using Interactables.Interobjects.DoorUtils; + using PlayerRoles.PlayableScps.Scp096; + using UnityEngine; + + using static HarmonyLib.AccessTools; + + /// + /// Patches the delegate. + /// Fixes open doors getting easily broke. + /// Bug reported to NW (https://trello.com/c/6Nz7Isjm/4637-scp096-easily-breaking-opened-doors). + /// + [HarmonyPatch(typeof(Scp096HitHandler), nameof(Scp096HitHandler.CheckDoorHit))] + internal class NWFixScp096BreakingDoor + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label ret = generator.DefineLabel(); + int offset = -4; + int index = newInstructions.FindIndex(x => x.operand == (object)Method(typeof(IDamageableDoor), nameof(IDamageableDoor.ServerDamage))) + offset; + + newInstructions.InsertRange(index, new[] + { + new CodeInstruction(OpCodes.Ldloc_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Call, Method(typeof(NWFixScp096BreakingDoor), nameof(IsFullyOpen))), + new(OpCodes.Brtrue_S, ret), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(ret); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + + private static bool IsFullyOpen(IDamageableDoor damageableDoor) => damageableDoor is BreakableDoor breakableDoor && breakableDoor.GetExactState() is 1; + } +} diff --git a/Exiled.Events/Patches/Fixes/PositionSpawnScp0492Fix.cs b/Exiled.Events/Patches/Fixes/PositionSpawnScp0492Fix.cs index 624dc88af6..e7a33157f1 100644 --- a/Exiled.Events/Patches/Fixes/PositionSpawnScp0492Fix.cs +++ b/Exiled.Events/Patches/Fixes/PositionSpawnScp0492Fix.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Fixes using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.Events.EventArgs.Player; using HarmonyLib; diff --git a/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs b/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs index 2722c59e10..ea6fdd606f 100644 --- a/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs +++ b/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; using PlayerRoles.PlayableScps.Scp3114; using PlayerRoles.PlayableScps.Subroutines; diff --git a/Exiled.Events/Patches/Fixes/VoiceChatMutesClear.cs b/Exiled.Events/Patches/Fixes/VoiceChatMutesClear.cs index 6e933e8bcf..f0fc6009bb 100644 --- a/Exiled.Events/Patches/Fixes/VoiceChatMutesClear.cs +++ b/Exiled.Events/Patches/Fixes/VoiceChatMutesClear.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Fixes/WeaponAttachmentDesyncFix.cs b/Exiled.Events/Patches/Fixes/WeaponAttachmentDesyncFix.cs index 5a2c4884f8..dfd304d2a5 100644 --- a/Exiled.Events/Patches/Fixes/WeaponAttachmentDesyncFix.cs +++ b/Exiled.Events/Patches/Fixes/WeaponAttachmentDesyncFix.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Fixes using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/AddRespawnTargetMultiplierConfig.cs b/Exiled.Events/Patches/Generic/AddRespawnTargetMultiplierConfig.cs new file mode 100644 index 0000000000..0054fd3a30 --- /dev/null +++ b/Exiled.Events/Patches/Generic/AddRespawnTargetMultiplierConfig.cs @@ -0,0 +1,54 @@ +// ----------------------------------------------------------------------- +// +// 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.Core.Generic.Pools; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + using Scp049Role = API.Features.Roles.Scp049Role; + + /// + /// Patches . + /// Adds the as NW config. + /// + [HarmonyPatch(typeof(RoundSummary), nameof(RoundSummary.ServerOnRespawned))] + internal class AddRespawnTargetMultiplierConfig + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + // replace "this.ChaosTargetCount += (int)((double)respawnedPlayers.Count * 0.75);" + // with " this.ChaosTargetCount += (int)((double)respawnedPlayers.Count * ConfigFile.ServerConfig.GetDouble("respawn_target_multiplier", 0.75);" + int offset = 0; + int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ldc_R8) + offset; + newInstructions.RemoveAt(index); + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // ConfigFile.ServerConfig.GetDouble("respawn_target_multiplier", 0.75); + new(OpCodes.Ldsfld, Field(typeof(GameCore.ConfigFile), nameof(GameCore.ConfigFile.ServerConfig))), + new(OpCodes.Ldstr, "respawn_target_multiplier"), + new(OpCodes.Ldc_R8, RoundSummary.RespawnTargetMultiplier), + new(OpCodes.Call, Method(typeof(YamlConfig), nameof(YamlConfig.GetDouble))), + }); + + 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.Events/Patches/Generic/CameraList.cs b/Exiled.Events/Patches/Generic/CameraList.cs index fbcb76e863..22f697d809 100644 --- a/Exiled.Events/Patches/Generic/CameraList.cs +++ b/Exiled.Events/Patches/Generic/CameraList.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Generic using API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/CanScp049SenseTutorial.cs b/Exiled.Events/Patches/Generic/CanScp049SenseTutorial.cs index 8f0f815ac0..0f9e3d49a2 100644 --- a/Exiled.Events/Patches/Generic/CanScp049SenseTutorial.cs +++ b/Exiled.Events/Patches/Generic/CanScp049SenseTutorial.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/CommandLogging.cs b/Exiled.Events/Patches/Generic/CommandLogging.cs index ec8b0d157c..d06cc60758 100644 --- a/Exiled.Events/Patches/Generic/CommandLogging.cs +++ b/Exiled.Events/Patches/Generic/CommandLogging.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/DestroyRecontainerInstance.cs b/Exiled.Events/Patches/Generic/DestroyRecontainerInstance.cs index 7b62568eb4..c778933d13 100644 --- a/Exiled.Events/Patches/Generic/DestroyRecontainerInstance.cs +++ b/Exiled.Events/Patches/Generic/DestroyRecontainerInstance.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/GeneratorList.cs b/Exiled.Events/Patches/Generic/GeneratorList.cs index eec846d6c3..248501613e 100644 --- a/Exiled.Events/Patches/Generic/GeneratorList.cs +++ b/Exiled.Events/Patches/Generic/GeneratorList.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/GhostModePatch.cs b/Exiled.Events/Patches/Generic/GhostModePatch.cs index 51401de0da..d6898e88ce 100644 --- a/Exiled.Events/Patches/Generic/GhostModePatch.cs +++ b/Exiled.Events/Patches/Generic/GhostModePatch.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using API.Features.Roles; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs b/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs index addee3ea8f..e6bf6753de 100644 --- a/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs +++ b/Exiled.Events/Patches/Generic/IndividualFriendlyFire.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Footprinting; @@ -89,7 +89,7 @@ public static bool CheckFriendlyFirePlayerRules(Footprint attackerFootprint, Ref // Return false, no custom friendly fire allowed, default to NW logic for FF. No point in processing if FF is enabled across the board. if (Server.FriendlyFire) - return HitboxIdentity.CheckFriendlyFire(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); + return HitboxIdentity.IsEnemy(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); // always allow damage from Server.Host if (attackerFootprint.Hub == Server.Host.ReferenceHub) @@ -98,7 +98,7 @@ public static bool CheckFriendlyFirePlayerRules(Footprint attackerFootprint, Ref // Only check friendlyFire if the FootPrint hasn't changed (Fix for Grenade not dealing damage because it's from a dead player) // TODO rework FriendlyFireRule to make it compatible with Footprint if (!attackerFootprint.SameLife(new(attackerFootprint.Hub))) - return HitboxIdentity.CheckFriendlyFire(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); + return HitboxIdentity.IsEnemy(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); if (attackerFootprint.Hub is null || victimHub is null) { @@ -170,14 +170,14 @@ public static bool CheckFriendlyFirePlayerRules(Footprint attackerFootprint, Ref Log.Error($"CheckFriendlyFirePlayerRules failed to handle friendly fire because: {ex}"); } - return HitboxIdentity.CheckFriendlyFire(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); + return HitboxIdentity.IsEnemy(attackerFootprint.Role, victimHub.roleManager.CurrentRole.RoleTypeId); } } /// - /// Patches . + /// Patches . /// - [HarmonyPatch(typeof(HitboxIdentity), nameof(HitboxIdentity.CheckFriendlyFire), typeof(ReferenceHub), typeof(ReferenceHub), typeof(bool))] + [HarmonyPatch(typeof(HitboxIdentity), nameof(HitboxIdentity.IsDamageable), typeof(ReferenceHub), typeof(ReferenceHub))] internal static class HitboxIdentityCheckFriendlyFire { private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) diff --git a/Exiled.Events/Patches/Generic/InitRecontainerInstance.cs b/Exiled.Events/Patches/Generic/InitRecontainerInstance.cs index eb6d3533d4..6e32ca1393 100644 --- a/Exiled.Events/Patches/Generic/InitRecontainerInstance.cs +++ b/Exiled.Events/Patches/Generic/InitRecontainerInstance.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/InventoryControlPatch.cs b/Exiled.Events/Patches/Generic/InventoryControlPatch.cs index 6651d46dd4..212ec2518c 100644 --- a/Exiled.Events/Patches/Generic/InventoryControlPatch.cs +++ b/Exiled.Events/Patches/Generic/InventoryControlPatch.cs @@ -14,8 +14,8 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; + using API.Features.Core.Generic.Pools; using API.Features.Items; - using API.Features.Pools; using Exiled.API.Features.Pickups; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/LockerList.cs b/Exiled.Events/Patches/Generic/LockerList.cs index 26b0732abe..bd616a847c 100644 --- a/Exiled.Events/Patches/Generic/LockerList.cs +++ b/Exiled.Events/Patches/Generic/LockerList.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/ParseVisionInformation.cs b/Exiled.Events/Patches/Generic/ParseVisionInformation.cs index 5698191921..a6dec99557 100644 --- a/Exiled.Events/Patches/Generic/ParseVisionInformation.cs +++ b/Exiled.Events/Patches/Generic/ParseVisionInformation.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/PickupControlPatch.cs b/Exiled.Events/Patches/Generic/PickupControlPatch.cs index ab8ba592f7..bcb689f8ab 100644 --- a/Exiled.Events/Patches/Generic/PickupControlPatch.cs +++ b/Exiled.Events/Patches/Generic/PickupControlPatch.cs @@ -11,8 +11,8 @@ namespace Exiled.Events.Patches.Generic using System.Collections.Generic; using System.Reflection.Emit; + using API.Features.Core.Generic.Pools; using API.Features.Pickups; - using API.Features.Pools; using Exiled.API.Features.Items; diff --git a/Exiled.Events/Patches/Generic/PlayerVolume.cs b/Exiled.Events/Patches/Generic/PlayerVolume.cs new file mode 100644 index 0000000000..fd7cf8730d --- /dev/null +++ b/Exiled.Events/Patches/Generic/PlayerVolume.cs @@ -0,0 +1,136 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using System.Collections.Generic; + using System.Linq; + using System.Reflection.Emit; + + using API.Features; + using API.Features.Core.Generic.Pools; + + using HarmonyLib; + + using PlayerRoles.Voice; + using UnityEngine; + using VoiceChat; + using VoiceChat.Codec; + using VoiceChat.Networking; + using VoiceChat.Playbacks; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Implements , using and . + /// + [HarmonyPatch(typeof(VoiceTransceiver), nameof(VoiceTransceiver.ServerReceiveMessage))] + internal static class PlayerVolume + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label skip = generator.DefineLabel(); + Label loopCheck = generator.DefineLabel(); + Label loopStart = generator.DefineLabel(); + + LocalBuilder plr = generator.DeclareLocal(typeof(Player)); + LocalBuilder svm = generator.DeclareLocal(typeof(StandardVoiceModule)); + LocalBuilder decoded = generator.DeclareLocal(typeof(float[])); + LocalBuilder pos = generator.DeclareLocal(typeof(int)); + + const int offset = 8; + int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(VoiceModuleBase), nameof(VoiceModuleBase.ValidateSend), new[] { typeof(VoiceChatChannel) }))) + offset; + + newInstructions.InsertRange(index, new List() + { + // Player plr = Player.Get(msg.Speaker) + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(VoiceMessage), nameof(VoiceMessage.Speaker))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + new(OpCodes.Stloc_S, plr), + + // if (plr.VoiceModule is StandardVoiceModule svm) + new(OpCodes.Ldloc_S, plr), + new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.VoiceModule))), + new(OpCodes.Isinst, typeof(StandardVoiceModule)), + new(OpCodes.Stloc_S, svm), + new(OpCodes.Ldloc_S, svm), + new(OpCodes.Brfalse_S, skip), + + // float[] decoded = new float[48000] + new(OpCodes.Ldc_I4, 48000), + new(OpCodes.Newarr, typeof(float)), + new(OpCodes.Stloc_S, decoded), + + // plr.VoiceModule.Decoder.Decode(msg.Data, msg.DataLength, decoded); + new(OpCodes.Ldloc_S, plr), + new(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.VoiceModule))), + new(OpCodes.Callvirt, PropertyGetter(typeof(VoiceModuleBase), nameof(VoiceModuleBase.Decoder))), + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(VoiceMessage), nameof(VoiceMessage.Data))), + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(VoiceMessage), nameof(VoiceMessage.DataLength))), + new(OpCodes.Ldloc_S, decoded), + new(OpCodes.Callvirt, Method(typeof(OpusDecoder), nameof(OpusDecoder.Decode), new[] { typeof(byte[]), typeof(int), typeof(float[]) })), + new(OpCodes.Pop), + + // for (int i = 0; i < array.Length; i++) + new(OpCodes.Ldc_I4_0), + new(OpCodes.Stloc_S, pos), + + // decoded[i] = Mathf.Abs(decoded[i]) + new(OpCodes.Br_S, loopCheck), + + // loop start + new CodeInstruction(OpCodes.Nop).WithLabels(loopStart), + new(OpCodes.Ldloc_S, decoded), + new(OpCodes.Ldloc_S, pos), + new(OpCodes.Ldloc_S, decoded), + new(OpCodes.Ldloc_S, pos), + new(OpCodes.Ldelem_R4), + new(OpCodes.Call, Method(typeof(Mathf), nameof(Mathf.Abs), new[] { typeof(float) })), + new(OpCodes.Stelem_R4), + + // i++ + new(OpCodes.Ldloc_S, pos), + new(OpCodes.Ldc_I4_1), + new(OpCodes.Add), + new(OpCodes.Stloc_S, pos), + + // i < array.Length + new CodeInstruction(OpCodes.Nop).WithLabels(loopCheck), + new(OpCodes.Ldloc_S, pos), + new(OpCodes.Ldloc_S, decoded), + new(OpCodes.Ldlen), + new(OpCodes.Conv_I4), + new(OpCodes.Blt_S, loopStart), + + // end loop -> standardVoiceModule.GlobalPlayback.Loudness = array.Sum() / (float)msg.DataLength; + new(OpCodes.Ldloc_S, svm), + new(OpCodes.Ldfld, Field(typeof(StandardVoiceModule), nameof(StandardVoiceModule.GlobalPlayback))), + new(OpCodes.Ldloc_S, decoded), + new(OpCodes.Call, Method(typeof(Enumerable), nameof(Enumerable.Sum), new[] { typeof(IEnumerable) })), + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(VoiceMessage), nameof(VoiceMessage.DataLength))), + new(OpCodes.Conv_R4), + new(OpCodes.Div), + new(OpCodes.Callvirt, PropertySetter(typeof(VoiceChatPlaybackBase), nameof(VoiceChatPlaybackBase.Loudness))), + + // skip + new CodeInstruction(OpCodes.Nop).WithLabels(skip), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} diff --git a/Exiled.Events/Patches/Generic/RoomList.cs b/Exiled.Events/Patches/Generic/RoomList.cs index 112410ceaf..934452bd7e 100644 --- a/Exiled.Events/Patches/Generic/RoomList.cs +++ b/Exiled.Events/Patches/Generic/RoomList.cs @@ -14,7 +14,7 @@ namespace Exiled.Events.Patches.Generic using API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using HarmonyLib; @@ -37,14 +37,14 @@ private static IEnumerable Transpiler(IEnumerable i.Calls(Method(typeof(RoomIdUtils), nameof(RoomIdUtils.PositionToCoords)))) + offset; - // Room.CreateComponent(gameObject); + // new Room(gameObject) newInstructions.InsertRange( index, new CodeInstruction[] { new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), new(OpCodes.Callvirt, PropertyGetter(typeof(Component), nameof(Component.gameObject))), - new(OpCodes.Call, Method(typeof(Room), nameof(Room.CreateComponent))), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(Room))[0]), }); for (int z = 0; z < newInstructions.Count; z++) diff --git a/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs b/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs index ddfb45adbe..c4d30be1f5 100644 --- a/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs +++ b/Exiled.Events/Patches/Generic/Scp049API/CallAbilityDuration.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp079API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp049; diff --git a/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityBaseCooldown.cs b/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityBaseCooldown.cs index a70583b5fd..d2edaefe0e 100644 --- a/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityBaseCooldown.cs +++ b/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityBaseCooldown.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp079API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityReducedCooldown.cs b/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityReducedCooldown.cs index 88c8333a1e..6cbc4e34fd 100644 --- a/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityReducedCooldown.cs +++ b/Exiled.Events/Patches/Generic/Scp049API/SenseAbilityReducedCooldown.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp079API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/Scp079Scan.cs b/Exiled.Events/Patches/Generic/Scp079Scan.cs index 4660d1923c..057a62782b 100644 --- a/Exiled.Events/Patches/Generic/Scp079Scan.cs +++ b/Exiled.Events/Patches/Generic/Scp079Scan.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/Scp106API/CooldownReductionReward.cs b/Exiled.Events/Patches/Generic/Scp106API/CooldownReductionReward.cs index 53517ee51a..c90c8f4e88 100644 --- a/Exiled.Events/Patches/Generic/Scp106API/CooldownReductionReward.cs +++ b/Exiled.Events/Patches/Generic/Scp106API/CooldownReductionReward.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp106API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp106; diff --git a/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs b/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs index 5bf4649584..3d90052792 100644 --- a/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs +++ b/Exiled.Events/Patches/Generic/Scp106API/CustomAttack.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp106API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp106; diff --git a/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs b/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs index 8cd89c0b1b..a839e342a7 100644 --- a/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs +++ b/Exiled.Events/Patches/Generic/Scp106API/HunterAtlastCostPerMetter.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp106API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp106; diff --git a/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs b/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs index cdb02f42ae..1ac1fbc145 100644 --- a/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs +++ b/Exiled.Events/Patches/Generic/Scp106API/SinkholeAbilityCooldown.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp106API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp106; diff --git a/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs b/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs index 38b7a9e53b..98392850e1 100644 --- a/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs +++ b/Exiled.Events/Patches/Generic/Scp106API/StalkVigorUse.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp106API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp106; diff --git a/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs b/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs index f14fd51d15..54ebf3bb5f 100644 --- a/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs +++ b/Exiled.Events/Patches/Generic/Scp106API/VigorRegeneration.cs @@ -10,7 +10,7 @@ namespace Exiled.Events.Patches.Generic.Scp106API using System.Collections.Generic; using System.Reflection.Emit; - using API.Features.Pools; + using API.Features.Core.Generic.Pools; using Exiled.API.Features; using HarmonyLib; using PlayerRoles.PlayableScps.Scp106; diff --git a/Exiled.Events/Patches/Generic/Scp173BeingLooked.cs b/Exiled.Events/Patches/Generic/Scp173BeingLooked.cs index 6e0e0d0be5..6c097cf7e7 100644 --- a/Exiled.Events/Patches/Generic/Scp173BeingLooked.cs +++ b/Exiled.Events/Patches/Generic/Scp173BeingLooked.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.Patches.Generic using System.Reflection.Emit; using API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using HarmonyLib; diff --git a/Exiled.Events/Patches/Generic/TargetOffset.cs b/Exiled.Events/Patches/Generic/TargetOffset.cs new file mode 100644 index 0000000000..a3435ff42e --- /dev/null +++ b/Exiled.Events/Patches/Generic/TargetOffset.cs @@ -0,0 +1,54 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using System.Collections.Generic; + using System.Reflection; + using System.Reflection.Emit; + + using API.Features; + using API.Features.Core.Generic.Pools; + + using HarmonyLib; + using Mirror; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// + [HarmonyPatch(typeof(RoundSummary), nameof(RoundSummary.SerializeSyncVars), typeof(NetworkWriter), typeof(bool))] + internal static class TargetOffset + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + static bool IsField(CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld + && (FieldInfo)instruction.operand == Field(typeof(RoundSummary), nameof(RoundSummary._chaosTargetCount)); + + List newInstructions = ListPool.Pool.Get(instructions); + + CodeInstruction[] addInstructions = new CodeInstruction[] + { + new(OpCodes.Call, PropertyGetter(typeof(Round), nameof(Round.TargetOffset))), + new(OpCodes.Add), + }; + + int offset = 1; + int index = newInstructions.FindIndex(IsField) + offset; + newInstructions.InsertRange(index, addInstructions); + + index = newInstructions.FindLastIndex(IsField) + offset; + newInstructions.InsertRange(index, addInstructions); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} diff --git a/Exiled.Installer/CommandSettings.cs b/Exiled.Installer/CommandSettings.cs index e65fadde43..f8fae601d4 100644 --- a/Exiled.Installer/CommandSettings.cs +++ b/Exiled.Installer/CommandSettings.cs @@ -168,7 +168,7 @@ internal sealed class CommandSettings /// public bool Exit { get; set; } - public async static Task Parse(string[] args) + public static async Task Parse(string[] args) { RootCommand.Handler = CommandHandler.Create(async args => await Program.MainSafe(args).ConfigureAwait(false)); RootCommand.TreatUnmatchedTokensAsErrors = false; diff --git a/Exiled.Installer/Program.cs b/Exiled.Installer/Program.cs index 1010e2098d..506bce5c4b 100644 --- a/Exiled.Installer/Program.cs +++ b/Exiled.Installer/Program.cs @@ -55,13 +55,13 @@ internal static class Program // Force use of LF because the file uses LF private static readonly Dictionary Markup = Resources.Markup.Trim().Split('\n').ToDictionary(s => s.Split(':')[0], s => s.Split(':', 2)[1]); - private async static Task Main(string[] args) + private static async Task Main(string[] args) { Console.OutputEncoding = new UTF8Encoding(false, false); await CommandSettings.Parse(args).ConfigureAwait(false); } - internal async static Task MainSafe(CommandSettings args) + internal static async Task MainSafe(CommandSettings args) { bool error = false; try @@ -111,10 +111,8 @@ internal async static Task MainSafe(CommandSettings args) Console.WriteLine(Resources.Program_MainSafe_Asset_found_); Console.WriteLine(FormatAsset(exiledAsset)); - using HttpClient httpClient = new() - { - Timeout = TimeSpan.FromSeconds(SecondsWaitForDownload), - }; + using HttpClient httpClient = new(); + httpClient.Timeout = TimeSpan.FromSeconds(SecondsWaitForDownload); httpClient.DefaultRequestHeaders.Add("User-Agent", Header); using HttpResponseMessage downloadResult = await httpClient.GetAsync(exiledAsset.BrowserDownloadUrl).ConfigureAwait(false); @@ -144,12 +142,12 @@ internal async static Task MainSafe(CommandSettings args) Environment.Exit(error ? 1 : 0); } - private async static Task> GetReleases() + private static async Task> GetReleases() { IEnumerable releases = (await GitHubClient.Repository.Release.GetAll(RepoID).ConfigureAwait(false)) .Where( r => Version.TryParse(r.TagName, out Version version) - && (version > VersionLimit)); + && version > VersionLimit); return releases.OrderByDescending(r => r.CreatedAt.Ticks); } @@ -267,7 +265,8 @@ static PathResolution TryParse(string s) { return TryParse(pair.Value); } - else if (!fileInFolder && !isFolder && + + if (!fileInFolder && !isFolder && pair.Key.Equals(fileName, StringComparison.OrdinalIgnoreCase)) { return TryParse(pair.Value); @@ -280,20 +279,27 @@ static PathResolution TryParse(string s) private static Release FindRelease(CommandSettings args, IEnumerable releases) { Console.WriteLine(Resources.Program_TryFindRelease_Trying_to_find_release__); - Version targetVersion = args.TargetVersion is not null ? new Version(args.TargetVersion) : new Version(releases.First().TagName); + Version? targetVersion = args.TargetVersion is not null ? new Version(args.TargetVersion) : null; - foreach (Release r in releases) - { - if (targetVersion != new Version(r.TagName)) - continue; + List enumerable = releases.ToList(); - if (targetVersion.IsPreRelease && !args.PreReleases) - continue; + foreach (Release release in enumerable) + { + if (targetVersion != null) + { + if (targetVersion == new Version(release.TagName)) + return release; + } + else + { + if (release.Prerelease && !args.PreReleases) + continue; - return r; + return release; + } } - return releases.First(); + return enumerable.First(); } } } \ No newline at end of file diff --git a/Exiled.Loader/AutoUpdateFiles.cs b/Exiled.Loader/AutoUpdateFiles.cs index c805536603..5caaabb915 100644 --- a/Exiled.Loader/AutoUpdateFiles.cs +++ b/Exiled.Loader/AutoUpdateFiles.cs @@ -17,6 +17,6 @@ public static class AutoUpdateFiles /// /// Gets which SCP: SL version generated Exiled. /// - public static readonly Version RequiredSCPSLVersion = new(13, 3, 0, 1); + public static readonly Version RequiredSCPSLVersion = new(13, 4, 0, 1); } } \ No newline at end of file diff --git a/Exiled.Loader/ConfigManager.cs b/Exiled.Loader/ConfigManager.cs index 17a351c9ee..4cfd46e192 100644 --- a/Exiled.Loader/ConfigManager.cs +++ b/Exiled.Loader/ConfigManager.cs @@ -19,7 +19,7 @@ namespace Exiled.Loader using Exiled.API.Features; using Exiled.API.Features.Attributes; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using YamlDotNet.Core; using Serialization = API.Features.EConfig; diff --git a/Exiled.Loader/Exiled.Loader.csproj b/Exiled.Loader/Exiled.Loader.csproj index 4a48187e26..3113c83351 100644 --- a/Exiled.Loader/Exiled.Loader.csproj +++ b/Exiled.Loader/Exiled.Loader.csproj @@ -30,7 +30,6 @@ - diff --git a/Exiled.Loader/GHApi/Models/Release.cs b/Exiled.Loader/GHApi/Models/Release.cs index aabc1aa590..d6ad929111 100644 --- a/Exiled.Loader/GHApi/Models/Release.cs +++ b/Exiled.Loader/GHApi/Models/Release.cs @@ -8,7 +8,9 @@ namespace Exiled.Loader.GHApi.Models { using System; + using System.Net.Http; using System.Runtime.Serialization; + using System.Threading.Tasks; using Utf8Json; @@ -47,6 +49,13 @@ namespace Exiled.Loader.GHApi.Models [DataMember(Name = "assets")] public readonly ReleaseAsset[] Assets; + /// + /// The release's description. + /// + public readonly string Description; + + private const string URL = "https://api.github.com/repos/Exiled-Team/EXILED/releases/tags/"; + /// /// Initializes a new instance of the struct. /// @@ -63,6 +72,20 @@ public Release(int id, string tag_name, bool prerelease, DateTime created_at, Re PreRelease = prerelease; CreatedAt = created_at; Assets = assets; + Description = string.Empty; + + using (HttpClient client = new HttpClient()) + { + HttpResponseMessage response = client.GetAsync(URL + TagName).GetAwaiter().GetResult(); + + if (!response.IsSuccessStatusCode) + return; + + string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + int startIndex = responseBody.IndexOf("\"body\":") + "\"body\":".Length; + int endIndex = responseBody.IndexOf("\"draft\":") - 2; + Description = responseBody.Substring(startIndex, endIndex - startIndex); + } } } } \ No newline at end of file diff --git a/Exiled.Loader/Loader.cs b/Exiled.Loader/Loader.cs index 56832a6a71..bff77523c9 100644 --- a/Exiled.Loader/Loader.cs +++ b/Exiled.Loader/Loader.cs @@ -19,10 +19,9 @@ namespace Exiled.Loader using API.Enums; using API.Interfaces; - using CommandSystem.Commands.Shared; - using Exiled.API.Features; + using Exiled.API.Features.Attributes; using Features; /// @@ -148,10 +147,14 @@ public static Assembly LoadAssembly(string path) /// Returns the created plugin instance or . public static IPlugin CreatePlugin(Assembly assembly) { + Type defaultPlayerClass = null; try { foreach (Type type in assembly.GetTypes()) { + if (typeof(Player).IsAssignableFrom(type)) + defaultPlayerClass = type; + if (type.IsAbstract || type.IsInterface) { Log.Debug($"\"{type.FullName}\" is an interface or abstract class, skipping."); @@ -197,6 +200,14 @@ public static IPlugin CreatePlugin(Assembly assembly) if (CheckPluginRequiredExiledVersion(plugin)) continue; + if (defaultPlayerClass is not null) + { + DefaultPlayerClassAttribute dpc = Player.DEFAULT_PLAYER_CLASS.GetCustomAttribute(); + + if (dpc is not null && !dpc.EnforceAuthority) + Player.DEFAULT_PLAYER_CLASS = defaultPlayerClass; + } + return plugin; } } diff --git a/Exiled.Loader/TranslationManager.cs b/Exiled.Loader/TranslationManager.cs index 06fffb153b..d5873bebee 100644 --- a/Exiled.Loader/TranslationManager.cs +++ b/Exiled.Loader/TranslationManager.cs @@ -17,7 +17,7 @@ namespace Exiled.Loader using API.Interfaces; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using YamlDotNet.Core; diff --git a/Exiled.Loader/Updater.cs b/Exiled.Loader/Updater.cs index f5555218f8..7e6453eb7f 100644 --- a/Exiled.Loader/Updater.cs +++ b/Exiled.Loader/Updater.cs @@ -31,7 +31,7 @@ namespace Exiled.Loader /// internal sealed class Updater { - private const long REPOID = 231269519; + private const long REPO_ID = 231269519; private const string INSTALLER_ASSET_NAME_LINUX = "Exiled.Installer-Linux"; private const string INSTALLER_ASSET_NAME_WIN = "Exiled.Installer-Win.exe"; @@ -136,10 +136,10 @@ private HttpClient CreateHttpClient() /// /// Finds an update using the client. /// - /// is the HTTP Client. - /// if the detection was forced. - /// if there is a new version of EXILED. - /// Returns true if there is an update, otherwise false. + /// The HTTP Client. + /// Whether the detection was forced. + /// Whether there is a new version of EXILED. + /// Whether there is an update. private bool FindUpdate(HttpClient client, bool forced, out NewVersion newVersion) { try @@ -148,7 +148,7 @@ private bool FindUpdate(HttpClient client, bool forced, out NewVersion newVersio Log.Info($"Found the smallest version of Exiled - {smallestVersion.Library.GetName().Name}:{smallestVersion.Version}"); - TaggedRelease[] releases = TagReleases(client.GetReleases(REPOID, new GetReleasesSettings(50, 1)).GetAwaiter().GetResult()); + TaggedRelease[] releases = TagReleases(client.GetReleases(REPO_ID, new GetReleasesSettings(50, 1)).GetAwaiter().GetResult()); if (FindRelease(releases, out Release targetRelease, smallestVersion, forced)) { if (!FindAsset(InstallerName, targetRelease, out ReleaseAsset asset)) @@ -158,7 +158,7 @@ private bool FindUpdate(HttpClient client, bool forced, out NewVersion newVersio } else { - Log.Info($"Found asset - Name: {asset.Name} | Size: {asset.Size} Download: {asset.BrowserDownloadUrl}"); + Log.Info($"Found asset - Name: {asset.Name} | Size: {asset.Size} | Download: {asset.BrowserDownloadUrl}"); newVersion = new NewVersion(targetRelease, asset); return true; } @@ -171,7 +171,7 @@ private bool FindUpdate(HttpClient client, bool forced, out NewVersion newVersio } catch (Utf8Json.JsonParsingException) { - Log.Error("Encountered GitHub ratelimit, unable to check and download the latest version of Exiled."); + Log.Error("Encountered GitHub rate limit, unable to check and download the latest version of Exiled."); } catch (Exception ex) { @@ -185,8 +185,8 @@ private bool FindUpdate(HttpClient client, bool forced, out NewVersion newVersio /// /// Updates the client's version of Exiled. /// - /// is the HTTP Client. - /// is the updated version of Exiled. + /// The HTTP Client. + /// The updated version of Exiled. private void Update(HttpClient client, NewVersion newVersion) { try @@ -272,7 +272,7 @@ private void Update(HttpClient client, NewVersion newVersion) /// /// Gets the releases of Exiled. /// - /// gets the array of releases that has been made. + /// Gets the array of releases that has been made. /// The last item in the array, which is the newest version of Exiled. private TaggedRelease[] TagReleases(Release[] releases) { @@ -284,21 +284,22 @@ private TaggedRelease[] TagReleases(Release[] releases) } /// - /// Is able to find the release specificed. + /// Finds the latest release. /// - /// is the list of releases (array). - /// is the most recent release of Exiled. - /// finds the smallest version of the Exiled Library. - /// if this update was forced or not. - /// the if the specific release was found or not. + /// The list of releases (array). + /// The most recent release of Exiled. + /// Finds the smallest version of the Exiled Library. + /// Whether this update was forced or not. + /// Whether the specific release was found. private bool FindRelease(TaggedRelease[] releases, out Release release, ExiledLib smallestVersion, bool forced = false) { bool includePRE = config.ShouldDownloadTestingReleases || ExiledLib.Any(l => l.Version.PreRelease is not null); - + Version gameVersion = new(GameCore.Version.Major, GameCore.Version.Minor, GameCore.Version.Revision); for (int z = 0; z < releases.Length; z++) { TaggedRelease taggedRelease = releases[z]; - if (taggedRelease.Release.PreRelease && !includePRE) + + if (!taggedRelease.Release.Description.Contains($"[Game Version: {gameVersion}]") || (taggedRelease.Release.PreRelease && !includePRE)) continue; if (taggedRelease.Version > smallestVersion.Version || forced) @@ -315,10 +316,10 @@ private bool FindRelease(TaggedRelease[] releases, out Release release, ExiledLi /// /// Finds the specified asset. /// - /// passes in the specified asset name. - /// passes in the release version. - /// is the asset that is tied to the release. - /// if it was able to find the asset or not. + /// Passes in the specified asset name. + /// Passes in the release version. + /// The asset that is tied to the release. + /// Whether it was able to find the asset. private bool FindAsset(string assetName, Release release, out ReleaseAsset asset) { for (int z = 0; z < release.Assets.Length; z++) diff --git a/Exiled.Permissions/Commands/Permissions/Group/Group.cs b/Exiled.Permissions/Commands/Permissions/Group/Group.cs index 0e91dbeb94..8d6474c14d 100644 --- a/Exiled.Permissions/Commands/Permissions/Group/Group.cs +++ b/Exiled.Permissions/Commands/Permissions/Group/Group.cs @@ -12,7 +12,7 @@ namespace Exiled.Permissions.Commands.Permissions.Group using CommandSystem; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Extensions; diff --git a/Exiled.Permissions/Commands/Permissions/Permissions.cs b/Exiled.Permissions/Commands/Permissions/Permissions.cs index 3bb84e73ee..6ee878cf7b 100644 --- a/Exiled.Permissions/Commands/Permissions/Permissions.cs +++ b/Exiled.Permissions/Commands/Permissions/Permissions.cs @@ -12,7 +12,7 @@ namespace Exiled.Permissions.Commands.Permissions using CommandSystem; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; /// /// Handles commands about permissions. diff --git a/Exiled.Permissions/Extensions/Permissions.cs b/Exiled.Permissions/Extensions/Permissions.cs index fba930746f..7bbce8617a 100644 --- a/Exiled.Permissions/Extensions/Permissions.cs +++ b/Exiled.Permissions/Extensions/Permissions.cs @@ -17,7 +17,7 @@ namespace Exiled.Permissions.Extensions using Exiled.API.Extensions; using Exiled.API.Features; - using Exiled.API.Features.Pools; + using Exiled.API.Features.Core.Generic.Pools; using Features; using Properties; diff --git a/Exiled/Exiled.nuspec b/Exiled/Exiled.nuspec index 77d8ad872d..04c0e2cd99 100644 --- a/Exiled/Exiled.nuspec +++ b/Exiled/Exiled.nuspec @@ -43,11 +43,8 @@ - - - - - + + diff --git a/Localization/README-BR.md b/Localization/README-BR.md index 3344a01af2..824b2b055c 100644 --- a/Localization/README-BR.md +++ b/Localization/README-BR.md @@ -1,15 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + +
EXILED é uma estrutura para plug-ins de alto nível aos servidores SCP: Secret Laboratory. Ele oferece um sistema de eventos para os desenvolvedores usarem para manipular ou alterar o código do jogo ou implementar suas próprias funções. Todos os eventos do EXILED são codificados com Harmony, o que significa que não requerem edição direta dos Assemblies do servidor para funcionar, o que permite dois benefícios exclusivos. diff --git a/Localization/README-CS.md b/Localization/README-CS.md index 03cbaa936c..69ea25eecd 100644 --- a/Localization/README-CS.md +++ b/Localization/README-CS.md @@ -1,15 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + +
EXILED je vysokoúrovňové rozhraní pro pluginy na servery hry SCP: Secret Laboratory. Nabízí systém "eventů", tedy událostí, které mohou vývojáři využít k manipulaci nebo změně herního kódu či implementaci vlastních funkcí. Všechny EXILED eventy jsou kódovány pomocí Harmony, což znamená, že ke svému fungování nevyžadují přímé úpravy serverových sestav, což přináší dvě jedinečné výhody. diff --git a/Localization/README-DK.md b/Localization/README-DK.md index 62e0245901..c4a3d05760 100644 --- a/Localization/README-DK.md +++ b/Localization/README-DK.md @@ -1,15 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - - GitHub Releases - -![Github Alle Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + +
EXILED er et plugin-framework på højt niveau til SCP: Secret Laboratory-servere. Det tilbyder et event-system, som udviklere kan bruge til at manipulere eller ændre spilkoden eller implementere deres egne funktioner. Alle EXILED-hændelser er kodet med Harmony, hvilket betyder, at de ikke kræver direkte redigering af server Assemblies for at fungere, hvilket giver to unikke fordele. @@ -157,4 +157,4 @@ Derfor **SKAL** plugins, der understøtter dynamiske opdateringer, følge disse Men ikke alle plugins behøver at understøtte dynamiske opdateringer. Hvis du ikke har tænkt dig at understøtte dynamiske opdateringer, er det helt fint, du skal bare ikke ændre Assembly Name på dit plugin, når du bygger en ny version, så behøver du ikke bekymre dig om noget af dette, bare sørg for, at serverværterne ved, at de bliver nødt til at genstarte deres servere fuldstændigt for at opdatere dit plugin. -**Translator: @misfiy** \ No newline at end of file +**Translator: @misfiy** diff --git a/Localization/README-ES.md b/Localization/README-ES.md index cbcdef49cb..23a2c09be9 100644 --- a/Localization/README-ES.md +++ b/Localization/README-ES.md @@ -1,14 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/galaxy119/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/galaxy119/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + + +
EXILED es una plataforma de desarrollo de plugins para servidores de SCP: Secret Laboratory. Ofrece un sistema de eventos para desarrolladores y poder modificar o cambiar código del juego, o implementar sus propias funciones. diff --git a/Localization/README-IT.md b/Localization/README-IT.md index 3fc3b085b3..9398aed7c6 100644 --- a/Localization/README-IT.md +++ b/Localization/README-IT.md @@ -1,14 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + + +
EXILED è un framework di alto livello per i server di SCP: Secret Laboratory. Offre un sistema di eventi per gli sviluppatori per modificare il codice di gioco o implementare le proprie funzioni. @@ -158,4 +159,4 @@ Nota che ho detto *nuove* assembly. Se sostituisci un'assembly con un'altra con Pertanto, i plugin che supportano gli Aggiornamenti Dinamici ***DEVONO*** seguire queste linee guida o verranno rimossi dal server Discord a causa del potenziale rischio per gli host del server. -Ma non tutti i plugin devono supportare gli Aggiornamenti Dinamici. Se non hai intenzione di supportare gli Aggiornamenti Dinamici, va benissimo, basta che non cambi il nome dell'Assembly del tuo plugin quando crei una nuova versione e non dovrai preoccuparti di nulla. Assicurati solo che gli host del server siano consapevoli che dovranno riavviare completamente i loro server per aggiornare il tuo plugin. \ No newline at end of file +Ma non tutti i plugin devono supportare gli Aggiornamenti Dinamici. Se non hai intenzione di supportare gli Aggiornamenti Dinamici, va benissimo, basta che non cambi il nome dell'Assembly del tuo plugin quando crei una nuova versione e non dovrai preoccuparti di nulla. Assicurati solo che gli host del server siano consapevoli che dovranno riavviare completamente i loro server per aggiornare il tuo plugin. diff --git a/Localization/README-PL.md b/Localization/README-PL.md index e962ffdcf1..25704480c1 100644 --- a/Localization/README-PL.md +++ b/Localization/README-PL.md @@ -1,15 +1,15 @@ - # EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + +
EXILED to wysoko poziomowy framework do tworzenia pluginów dla serwerów w grze SCP: Secret Laboratory. Oferuje on system zdarzeń, do którego programiści mogą podpinać swój kod w celu manipulacji bądź zmiany działania gry, lub implementowania własnych funkcji. Wszystkie zdarzenia EXILED'a są zaprogromowane za pomocą Harmony, co oznacza że nie wymagają bezpośredniego modyfikowania serwerowych plików Assembly, co daje dwie wyjątkowe korzyści. diff --git a/Localization/README-TR.md b/Localization/README-TR.md new file mode 100644 index 0000000000..dbc09a66e9 --- /dev/null +++ b/Localization/README-TR.md @@ -0,0 +1,153 @@ +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) + + Chat on Discord + + +
+ +EXILED, SCP: Secret Laboratory sunucuları için yüksek düzeyde bir Framework yani bir Yazılım iskeleti'dir. Geliştiricilere oyun kodunu değiştirmek veya kendi fonksiyonlarını eklemek için kullanabilecekleri bir olay sistemi sunar. Tüm EXILED eventleri(olayları) Harmony kullanılarak oluşturulmuştur, bu da demek oluyor ki eventlerin(olayların) işlevsel olabilmesi için doğrudan sunucu kodunu değiştirmenize gerek yoktur ve bu durum 2 avantaj sağlar: + + - İlk olarak. Framework (Yazılım iskeleti)'nin özgürce yayımlanabilir ve dağıtılabilir olmasıdır, buda geliştiricilere nasıl çalıştığını daha iyi anlama imkanını sunar ve ek olarak fonksiyonları ekleme, değiştirme yapmalarına olanak tanır. + - İkinci olarak, Framework (Yazılım iskeleti) tüm kodun, sunucu kodunun dışına çıktığı için küçük oyun güncellemeleri Framework (Yazılım iskeleti)'ne çok az etki yapar, ve eğer gerek varsa güncelleme yapılması kolaylaşır. + + +# İndirme +EXILED'ı indirmek oldukça kolaydır. EXILED kendini Northwood'un Plugin API'si üzerinden yükler, bu nedenle ``Exiled.tar.gz`` dosyasının içinde iki klasör bulunmaktadır. ``SCP Secret Laboratory`` dosyasının içinde, ``EXILED`` klasöründeki eklentileri yüklemek için gerekli dosyalar bulunur. Yapmanız gereken tek şey bu iki dosyayı doğru konuma yerleştirmektir. Bu nasıl yapılacağı aşağıda belirtilmiştir. + +Eğer kurulum programını kullanmayı seçerseniz ve doğru bir şekilde çalıştırırsanız, bütün EXILED özelliklerini yüklemekle ilgili işlemleri otomatik olarak gerçekleştirir. + +# Windows +### Otomatik indirme ([Daha fazla bilgi için tıkla](https://github.com/Exiled-Team/EXILED/blob/master/Exiled.Installer/README.md)) +**Not**: Sunucuyu indirdiğin kullanıcıda olduğundan emin ol veya yönetici olduğundan emin ol. + + - **`Exiled.Installer-Win.exe`'i [Buradan indir](https://github.com/Exiled-Team/EXILED/releases)** (Assets'e tıkla -> Installer'ı indir) + - İndirdiğinizde sunucu klasörünüze yerleştirin. (Eğer indirmediyseniz, sunucuyu indirin.) + - **`Exiled.Installer.exe`**'e iki kere tıkla ve aç veya **[tıklayarak .bat'ı indir](https://www.dropbox.com/s/xny4xus73ze6mq9/install-prerelease.bat?dl=1)** En son ön yayınını yüklemek için sunucu klasörünün içine koy. + - Eklenti indirmek için [Eklenti indirme](#Eklenti-İndirme) Kısmına göz gezdir. +**Not:** eğer EXILED'ı uzaktan bağlantılı olan bir sunucuya indiriyor iseniz .exe'yi Sunucu açtığınız kullanıcı ile aynı olduğundan emin oluyon veya Yönetici izinleri verin. + +### Manuel indirme + - **`Exiled.tar.gz` ['yi buradan indir](https://github.com/Exiled-Team/EXILED/releases)** + - İçeriğini [7Zip](https://www.7-zip.org/) veya [WinRar](https://www.win-rar.com/download.html?&L=6) ile çıkartın. + - **``EXILED``** Klasörünü **`%appdata%`** ya taşıyın *Not: Bu klasör ``C:\Users\(Kullanıcı_ismi)\AppData\Roaming``, ve ``C:\Users\(Kullanıcı_ismi)\AppData\Roaming\SCP Secret Laboratory``**'nin içinde değil!**, ve (...)\AppData\Roaming'**de olması zorunludur**. (...)\AppData*'YA DEĞİL! + - **``SCP Secret Laboratory``**Klasörünü **`%appdata%`**'ya taşı. + - Windows 10 ve 11: + Cortanaya / Arama simgesine veya Windows Explorer çubuğuna `%appdata%` yazın. + - Diğer windows sürümleri + Win + R tuşlarına basın ve `%appdata%` yazın. + +### Eklenti İndirme +Bu kadar, EXILED şimdi sunucunuzda kuruldu ve bir sonraki sunucu başladığında aktif olmalıdır. Unutmayın ki EXILED kendi başına neredeyse hiçbir şey yapmaz, bu yüzden yeni eklentileri **[Discord](https://discord.gg/PyUkWTg)** sunucumuzdan almayı unutmayın. +- Bir Eklenti indirmek için aşağıdaki talimatları okuyun: + - Bir eklentiyi indirmek için [*Onun* releases (yayınlanma) sayfasına gidin](https://i.imgur.com/u34wgPD.jpg) (**`.dll` UZANTILI OLMALIDIR**) + - İndirdiğiniz eklentiyi: ``C:\Users\(Kullanıcı_ismi)\AppData\Roaming\EXILED\Plugins``'dizinine taşıyın (Win + R Tuşlarına basarak `%appdata%` yazarak buraya taşıyabilirsiniz) + +# Linux +### Otomatik indirme ([Daha fazla bilgi için tıkla](https://github.com/Exiled-Team/EXILED/blob/master/Exiled.Installer/README.md)) + +**Not:** EXILED'ı uzaktan bağlanılan bir sunucuya indiriyor iseniz, indirme programını sunucuyu kurduğunuz kullanıcı ile açın veya (root) yetkiniz olması gerekir. + + - **`Exiled.Installer-Linux`'u [Buradan indir](https://github.com/Exiled-Team/EXILED/releases)** (Assets'e tıkla -> Installer'ı indir) + - Ya **`./Exiled.Installer-Linux --path /sunucuya/giden/klasör`** Yazarak ya da doğrudan sunucu klasörüne taşıyarak ve ardından terminalde (`cd` kullanarak) şu komutu yazarak yükleyin: **`./Exiled.Installer-Linux`**. + - eğer en yeni ön yayını istiyor iseniz **`--pre-releases`** ekle. Örnek: **`./Exiled.Installer-Linux /sunucuya/giden/klasör --pre-releases`** + - Başka bir örnek: `eğer Exiled.Installer-Linux` dosyası sunucu klasörüne yerleştirdiyseniz `/sunucuya/giden/klasör/Exiled.Installer-Linux --pre-releases` + - Eklenti indirmek için [Tıkla ve göz gezdir!](#Eklenti-Indirme) + +### Manuel indirme + - SCP sunucusunu açan kullanıcı olduğundan **Emin** ol + - **`Exiled.tar.gz` ['yi buradan indir](https://github.com/Exiled-Team/EXILED/releases)** (SSH: sağ tık yap ve `Exiled.tar.gz`'nin bağlantısını al, ve **`wget (bağlantı)`** Komudunu yazın.) + - Bulunduğunuz klasöre çıkartmak için **``tar -xzvf EXILED.tar.gz``** Komudunu yazın. + - **`EXILED`** Klasörünü **``~/.config``**'e taşı. *Not: Bu klasör ``~/.config``'e gitmeli, ``~/.config/SCP Secret Laboratory``**'nin içine değil!** (SSH: **`mv EXILED ~/.config/`**) + - **`SCP Secret Laboratory`** Klasörünü **``~/.config``**'e taşı. *Not: Bu klasör ``~/.config``'nin içine gitmelidir, ``~/.config/SCP Secret Laboratory``**'nin içine değil!** (SSH: **`mv SCP Secret Laboratory ~/.config/`**) + +### Eklenti Indirme +Bu kadar, EXILED şimdi sunucunuzda kuruldu ve bir sonraki sunucu başladığında aktif olmalıdır. Unutmayın ki EXILED kendi başına neredeyse hiçbir şey yapamaz, bu yüzden yeni eklentileri **[Discord](https://discord.gg/PyUkWTg)** sunucumuzdan almayı unutmayın. +- Bir Eklenti indirmek için aşağıdaki talimatları okuyun: + - Bir eklentiyi indirmek için [*Onun* releases (yayınlanma) sayfasına gidin](https://i.imgur.com/u34wgPD.jpg) (**`.dll` UZANTILI OLMALIDIR**) + - İndirdiğiniz Eklentiyi: ``~/.config/EXILED/Plugins``'dizininine taşıyın (eğer SSH'i root olarak kullanıyor iseniz, o zaman doğru `.config`'i `/home/(SCP Server kullanıcısı)` dizininin içinde arayın.) + +# Config +EXILED kendi başına bazı yapılandırma seçenekleri sunar. +Bunların hepsi sunucu açıldığı zaman otomatik olarak yapılır, Bunlar şuradadır Linuxda: ``~/.config/EXILED/Configs/(serverportu)-config.yml`` Windows için: (``%AppData%\EXILED\Configs\(serverportu)-config.yml``). + +Eklenti Configleri(Yapılandırma seçenekleri) ``config_gameplay.txt`` Dosyasında **değildir** Onun yerine eklenti seçeneklieri Linuxda: ``~/.config/EXILED/Configs/(serverportu)-config.yml`` Windows için: (``%AppData%\EXILED\(serverportu)-config.yml``). +Ancak, bazı eklentiler kendi başlarına diğer yerlerden yapılandırma/ayarları alabilir. Bu klasör genellikle eklenti ayarlarının/bağlantılarının bulunduğu yerdir. Hatalar varsa lütfen ilgili eklenti geliştiricisine başvurun. + +# Geliştiriciler için + +Eğer EXILED için bir eklenti yapmak istiyorsanız, bunu yapmak oldukça basittir [Daha fazla bilgi için tıkla!](https://github.com/Exiled-Team/EXILED/blob/master/GettingStarted.md). + +Daha kapsamlı ve sürekli güncellenen öğreticiler için [EXILED websitesine](https://exiled-team.github.io/EXILED/articles/install.html) göz atın. + +Ama pluginlerini herkese açık yaparken bu kuralları takip etmekte fayda var: + + - Eklentiniz ``Exiled.API.Features.Plugin<>`` sınıfından türetilmiş bir sınıf içermelidir; aksi halde EXILED sunucu başladığında eklentinizi yüklemeyecektir. + - Bir eklenti yüklendiğinde, yukarıda bahsedilen sınıfın ``OnEnabled()`` yöntemindeki kod hemen yürütülür; diğer eklentilerin yüklenmesini beklemez. Sunucu başlatma sürecinin tamamlanmasını beklemez. Hiçbir şeyi beklemez. ``OnEnabled()`` yönteminizi yaparken, sunucu henüz başlatılmamış olabilecek şeylere erişim sağlamadığınızdan emin olun, ÖRNEK: ServerConsole.Port veya PlayerManager.localPlayer vs... + - Eğer eklentiniz yüklendiğinde henüz başlatılmamış olan şeylere erişim sağlamanız gerekiyorsa, bunu yapmak için önerilen yol, bu işlemi gerçekleştirmek için ```WaitingForPlayers`` eventini(olayını) beklemektir. Eğer daha erken bazı işlemler yapmanız gerekiyorsa, kodunuzu devam etmeden önce gerekli değişkenin/nesnenin null olmadığını kontrol eden bir ``while(!x)`` döngüsü içine almanız önerilir. + - EXILED, yürütme sırasında eklenti derlemelerini dinamik olarak yeniden yükleme işlemini destekler. Bu, bir eklentiyi güncellemeniz gerektiğinde sunucuyu yeniden başlatmadan yapılabilir. Ancak, yürütme sırasında bir eklentiyi güncelliyorsanız, eklentinin bunu desteklemesi gerekmektedir; aksi halde sorunlarla karşılaşabilirsiniz. Daha fazla bilgi ve takip edilmesi gereken kurallar için ``Dinamik Güncelleme`` bölümüne başvurun. + - EXILED'da OnUpdate, OnFixedUpdate veya OnLateUpdate eventi(olayı) ***Bulunmamaktadır!***, Eğer sık sık çalışan bir kod calıştırmanız gerekiyor ise bir MEC coroutine Kullanabilirsiniz ki bu bir frame, 0.01f bekler ya da Timing.FixedUpdate gibi bir Timing katmanı kullanabilirsiniz. + ### MEC (More Effective Coroutines) (Eş zamanlı iş parçacığı) +Hiç MEC (More Effective Coroutines) kullanmadı iseniz işte size MEC kullanmanız için bir rehber! +MEC Coroutine'leri zamanlanmış yöntemlerdir. ve çalışan bir MEC kodunun kesilmeden / devre dışı bırakmadan önce belirli bir süre beklemenizi destekler +MEC Coroutine'leri Unity ile kullanılmak üzere güvenlidir AMA ***Unity ile etkileşimde bulunmak için yeni Threadler(iş parçacıkları) oluşturmayın!! sunucuyu çökertir.*** + +MEC Kullanmak için ``Assembly-CSharp-firstpass.dll``'yi referans etmeniz ve ``Using MEC;``'yi eklemeniz gerekir. + +HER DÖNGÜ ARASINDA 5 SANİYE BEKLEYEN BİR COROUTINE YAPIMI: + +```cs +using MEC; +using Exiled.API.Features; + +public void SomeMethod() +{ + Timing.RunCoroutine(MyCoroutine()); +} + +public IEnumerator MyCoroutine() +{ + for (;;) //aşağıdaki kodu sonsuza kadar çalıştırır. + { + Log.Info("ben bir döngüyüm!"); //Log.Info yu sunucu konsolunda bir satır yazmak için çağırıldı. + yield return Timing.WaitForSeconds(5f); //Bu coroutine'a 5 saniye beklemesini söyler, çünkü bu döngünün sonunda olduğu için, döngünün tekrarlanmasını etkili bir şekilde 5 saniye boyunca duraklatır. + } +} +``` + +Eğer MEC hakkında bilgi sahibi değilseniz ve daha fazla öğrenmek, tavsiye almak veya yardıma ihtiyacınız varsa, **kesinlikle** biraz Google'da araştırma yapmanız veya Discord'ta soru sormanız tavsiye edilir. Sorular, ne kadar 'saçma' olursa olsun, her zaman mümkün olan en yardımcı ve net şekilde cevaplanacaktır; bu, eklenti geliştiricilerinin daha iyi kod yazmalarına yardımcı olmak içindir. Daha iyi bir kod, herkes için daha iyidir. + +### Dinamik Güncelleme +EXILED, bir sunucu yeniden başlatma işlemine gerek olmadan eklenti derlemelerini dinamik olarak yeniden yükleme işlemini destekleyen bir Framework(Yazılım iskeleti)'dir. +Örneğin, sunucuyu sadece `Exiled.Events` eklentisiyle başlatırsanız ve yeni bir eklenti eklemek istiyorsanız, bu görevi tamamlamak için sunucuyu yeniden başlatmanıza gerek yoktur. Basitçe Remote Admin veya Sunucu Konsolu komutu olan ``reload plugins`` komutunu kullanarak, önce yüklenmemiş olan yeni eklentiler dahil olmak üzere tüm EXILED eklentilerini yeniden yükleyebilirsiniz. + +Bu aynı zamanda eklentileri tamamen yeniden başlatmadan *güncelleme* yapmanıza da olanak tanır. Ancak, bunun düzgün bir şekilde gerçekleşmesi için eklenti geliştiricisi tarafından takip edilmesi gereken birkaç kılavuz bulunmaktadır: + +***Sunucu sahipleri için*** + - Eğer bir eklentiyi güncelliyor iseniz emin olunki derlemenin adı şu anda yüklü olan sürüm ile (varsa) aynı değildir. bu işlem eklentiyi yapan Geliştirici tarafından Dinamik Güncelleme özelliği gözetilerek yapılmış olması gerekir, sadece dosya adını değiştirmek işe yaramaz. + - Eğer eklenti dinamik güncellemeleri destekliyorsa, yeni sürümü "Plugins" klasörüne koyarken, aynı zamanda eski sürümü de klasörden kaldırdığınızdan emin olun. EXILED'ı yeniden yüklemeden önce bunu sağlamamak, birçok kötü duruma yol açabilir. + - Dinamik olarak bir eklentiyi güncellemenin ortaya çıkardığı herhangi bir sorun, yalnızca sizin ve ilgili eklentinin geliştiricisinin sorumluluğundadır. EXILED dinamik güncellemeleri tamamen destekler ve teşvik eder; ancak, hata ihtimali, sunucu sahibi veya eklenti geliştiricisi tarafından yanlış bir şeyler yapıldığında ortaya çıkabilir. Dinamik güncellemelerle ilgili bir hata bildirmeden önce, her iki tarafın da işlemi doğru bir şekilde gerçekleştirdiğini doğrulayın. + + ***Geliştiriciler için*** + + - Dinamik güncellemeleri desteklemek isteyen eklentiler, devre dışı bırakıldıklarında veya yeniden yüklendiklerinde bağlı oldukları tüm olaylardan aboneliklerini(Subscribe) iptal etmeye dikkat etmelidir. + - Özel Harmony yamaları(patch) bulunan eklentiler, Harmony örneğinin adında bir değişken kullanmalı ve eklenti devre dışı bırakıldığında veya yeniden yüklendiğinde Harmony örneğini ``UnPatchAll()`` kullanarak iptal etmelidir. + - ``OnEnabled()`` içinde başlatılan herhangi bir coroutine, eklenti devre dışı bırakıldığında veya yeniden yüklendiğinde sonlandırılmalıdır / Bitirilmelidir. + +Bu işlemlerin hepsi, eklenti sınıfındaki ``OnReloaded()`` veya ``OnDisabled()`` yöntemlerinde gerçekleştirilebilir. EXILED eklentileri yeniden yüklediğinde, İlk olarak ``OnDisabled()``, ardından ``OnReloaded()``, daha sonra yeni derlemeleri yükler ve en son olarak ``OnEnabled()`` yöntemini çalıştırır. + + +Unutmayın ki: bu yeni derlemelerin olduğu anlamına gelir. Eğer aynı isimdeki bir derlemeyi başka bir Derleme ile değiştirir iseniz o derleme ***GÜNCELLENMEZ***. Bu, Global Assembly Cache (GAC) nedeniyledir. Eğer Önbellekte zaten var olan bir derlemeyi 'yüklemeye' çalışır iseniz her zaman önbellekteki derlemeyi kullanır + +Bu nedenle, eğer eklentiniz dinamik güncellemeleri destekliyorsa, her sürümü farklı bir derleme adıyla derlemelisiniz (dosyanın adını değiştirmek işe yaramaz). Ayrıca, eski derleme artık gerekli olmadığında "Silinmediği" için, olaylardan abonelik(Subscribe) iptal etmeyi, Harmony örneğinizi iptal etmeyi, coroutine'leri sonlandırmayı vs... unutmazsanız, bu kodun eski sürümü de yeni sürüm koduyla birlikte çalışmaya devam eder. Bu, gerçekleşmesine izin vermek için çok kötü bir fikirdir. + +Bu nedenle, dinamik güncellemeleri destekleyen eklentilerin bu yönergeleri takip etmeleri ***ZORUNLUDUR***; aksi halde potansiyel risk nedeniyle Discord sunucudan kaldırılabilirler. + +Her eklentinin dinamik güncellemeleri desteklemesi gerekmez. Eğer dinamik güncellemeleri desteklemeyi planlamıyorsanız, bu tamamen uygun bir durumdur. Sadece yeni bir sürüm oluştururken eklentinizin derleme adını değiştirmekten kaçının. Bu tür durumlarda, sunucu sahiplerinin eklentinizi güncellemek için sunucularını tamamen yeniden başlatmaları gerektiğini bildirin. + +çeviri: Enes Batur (@_funnyman_xdxdxd.rofl.) & Dogy (@.dogy69) diff --git "a/Localization/README-\320\240\321\203\321\201\321\201\320\272\320\270\320\271.md" "b/Localization/README-\320\240\321\203\321\201\321\201\320\272\320\270\320\271.md" index 47072a9722..fb05635990 100644 --- "a/Localization/README-\320\240\321\203\321\201\321\201\320\272\320\270\320\271.md" +++ "b/Localization/README-\320\240\321\203\321\201\321\201\320\272\320\270\320\271.md" @@ -1,15 +1,15 @@ -# EXILED - библиотека для разработки плагинов - -![EXILED CI](https://github.com/galaxy119/EXILED/workflows/EXILED%20CI/badge.svg?branch=2.0.0) - -GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/galaxy119/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) -Chat on Discord - + Chat on Discord + +
EXILED - низкоуровневый фреймворк для серверов SCP: Secret Laboratory. Он предлагает систему событий, которую разработчики могут использовать для различных манипуляций, изменения кода игры или реализации собственных функций. Все ивенты EXILED сделаны с помощью Harmony, это означает, что для их функционирования не требуется прямого редактирования серверных сборок, что позволяет получить два уникальных преимущества: - Во-первых, весь код фреймворка может быть свободно опубликован и распространен, что позволяет разработчикам лучше понять, *как* он работает, а также предложить свои предложения по дополнению или изменению его функций. diff --git "a/Localization/README-\344\270\255\346\226\207.md" "b/Localization/README-\344\270\255\346\226\207.md" index dd0600998e..c65af1c13c 100644 --- "a/Localization/README-\344\270\255\346\226\207.md" +++ "b/Localization/README-\344\270\255\346\226\207.md" @@ -1,14 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + + +
EXILED是一个用于SCP: 秘密实验室服务器的高级插件框架。 它为开发者提供了一个可以改变游戏代码或实现其自己的功能的事件系统。 diff --git a/README.md b/README.md index 3f063ced5f..483dd4e5f0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# EXILED - EXtended In-runtime Library for External Development - -![EXILED CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) - - GitHub Releases - -![Github All Downloads](https://img.shields.io/github/downloads/Exiled-Team/EXILED/total.svg?style=flat) -![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/dev) +

EXILED - EXtended In-runtime Library for External Development

+
+ +[CI](https://github.com/Exiled-Team/EXILED/actions/workflows/main.yml/badge.svg?branch=master) +GitHub Releases +Downloads +![Github Commits](https://img.shields.io/github/commit-activity/w/Exiled-Team/EXILED/apis-rework?style=for-the-badge&logo=git) - Chat on Discord - + Chat on Discord + +
EXILED is a high-level plugin framework for SCP: Secret Laboratory servers. It offers an event system for developers to hook into in order to manipulate or change game code or implement their own functions. All EXILED events are coded with Harmony, meaning they require no direct editing of server assemblies to function, which allows for two unique benefits. @@ -26,6 +26,7 @@ All EXILED events are coded with Harmony, meaning they require no direct editing - [Italiano](https://github.com/Exiled-Team/EXILED/blob/master/Localization/README-IT.md) - [Čeština](https://github.com/Exiled-Team/EXILED/blob/master/Localization/README-CS.md) - [Dansk](https://github.com/Exiled-Team/EXILED/blob/master/Localization/README-DK.md) +- [Türkçe](https://github.com/Exiled-Team/EXILED/blob/master/Localization/README-TR.md) # Installation Installation of EXILED is quite simple. It loads itself through Northwood’s Plugin API. That's why there are two folders inside the ``Exiled.tar.gz`` in release files. ``SCP Secret Laboratory`` contains the necessary files to load EXILED features in ``EXILED`` folder. All you need to do is move these two folders into the appropriate path, which are explained below, and you are done! diff --git a/build.ps1 b/build.ps1 index 2b4f5a54c8..24385b7400 100644 --- a/build.ps1 +++ b/build.ps1 @@ -11,8 +11,7 @@ $Projects = @( 'Exiled.Events', 'Exiled.CreditTags', 'Exiled.Example', - 'Exiled.CustomItems', - 'Exiled.CustomRoles' + 'Exiled.CustomModules' ) function Execute { diff --git a/docs/docs/Resources/Intro.md b/docs/docs/Resources/Intro.md index e75dfa1109..792feb654f 100644 --- a/docs/docs/Resources/Intro.md +++ b/docs/docs/Resources/Intro.md @@ -34,7 +34,7 @@ sidebar_position: 1
Roles -```md title="Latest Updated: 13.3.0.1" +```md title="Latest Updated: 13.4.0.1" | Id | RoleTypeId | Team | Side | LeadingTeam | |-----|----------------|------------------|------------------|-----------------| | -1 | None | Dead | None | Draw | @@ -70,7 +70,7 @@ sidebar_position: 1
Items -```md title="Latest Updated: 13.3.0.1" +```md title="Latest Updated: 13.4.0.1" [-1] None [0] KeycardJanitor [1] KeycardScientist @@ -136,7 +136,7 @@ sidebar_position: 1
Ammo -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] None [1] Nato556 [2] Nato762 @@ -151,7 +151,7 @@ sidebar_position: 1
Doors -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] UnknownDoor [1] Scp914Door [2] GR18Inner @@ -220,7 +220,7 @@ sidebar_position: 1
Rooms -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] Unknown [1] LczArmory [2] LczCurve @@ -283,7 +283,7 @@ sidebar_position: 1
Elevators -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] Unknown [1] GateA [2] GateB @@ -299,7 +299,7 @@ sidebar_position: 1
DamageType -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] Unknown [1] Falldown [2] Warhead @@ -381,7 +381,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Effects -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [-1] None [0] AmnesiaItems [1] AmnesiaVision @@ -432,7 +432,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Keycard Perms -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] None [1] Checkpoints [2] ExitGates @@ -453,7 +453,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Lock Type -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] None [1] Regular079 [2] Lockdown079 @@ -473,7 +473,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Structures -```md title="Latest Updated: 13.3.0.1" +```md title="Latest Updated: 13.4.0.1" [0] StandardLocker [1] LargeGunLocker [2] ScpPedestal @@ -488,7 +488,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Blood -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] Default [1] Scp106 [2] Spreaded @@ -501,7 +501,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
GeneratorState -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [1] None [2] Unlocked [4] Open @@ -515,7 +515,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Intercom States -```md title="Latest Updated: 13.3.0.1" +```md title="Latest Updated: 13.4.0.1" [0] Ready [1] Starting [2] InUse @@ -529,7 +529,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
BroadcastFlags -```md title="Latest Updated: 13.3.0.1" +```md title="Latest Updated: 13.4.0.1" [0] Normal [1] Truncated [2] AdminChat @@ -543,7 +543,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Attachment Names -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] None [1] IronSights [2] DotSight @@ -598,7 +598,7 @@ PlayerStatsSystem::Scp018DamageHandler : AttackerDamageHandler
Spawn Reasons -```md title="Latest Updated: 8.4.3.0" +```md title="Latest Updated: 8.7.1.0" [0] None [1] RoundStart [2] LateJoin