diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 7ee77d48a0..ab7b2d0556 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -16,9 +16,9 @@ defaults: working-directory: ./EXILED env: - EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Dev.zip + EXILED_REFERENCES_URL: https://exslmod-team.github.io/SL-References/Dev.zip EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References - EXILED_DLL_ARCHIVER_URL: https://github.com/ExMod-Team/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe + EXILED_DLL_ARCHIVER_URL: https://github.com/ExSLMod-Team/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe jobs: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f4d75022e9..728da76f7f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,7 +12,7 @@ permissions: id-token: write env: - EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Master.zip + EXILED_REFERENCES_URL: https://exslmod-team.github.io/SL-References/Master.zip EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. diff --git a/.github/workflows/labapi.yml b/.github/workflows/labapi.yml new file mode 100644 index 0000000000..69cb71f0aa --- /dev/null +++ b/.github/workflows/labapi.yml @@ -0,0 +1,74 @@ +name: Exiled Dev CI + +on: + push: + branches: + - LabAPI + pull_request: + branches: + - LabAPI + workflow_dispatch: + +defaults: + run: + working-directory: ./EXILED + +env: + EXILED_REFERENCES_URL: https://exslmod-team.github.io/SL-References/LabAPI.zip + EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References + EXILED_DLL_ARCHIVER_URL: https://github.com/ExSLMod-Team/EXILED-DLL-Archiver/releases/download/v1.8.2/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 != github.event.pull_request.base.repo.full_name + + steps: + + - name: Setup .NET Core SDK + uses: actions/setup-dotnet@v4.0.1 + + - name: Setup Nuget + uses: nuget/setup-nuget@v2 + + - uses: actions/checkout@v4.1.7 + + - name: Get references + shell: pwsh + run: | + Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/EXILED/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 'ExMod.Exiled.*.nupkg' -Recurse).Name + Out-File -FilePath ${{ github.env }} -InputObject "PackageFile=$File" -Encoding utf-8 -Append + + - name: Upload nuget package + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PackageFile }} + path: EXILED/${{ env.PackageFile }} + + - name: Get references + shell: pwsh + run: | + Invoke-WebRequest -Uri ${{ env.EXILED_DLL_ARCHIVER_URL }} -OutFile ${{ github.workspace }}/EXILED/EXILED-DLL-Archiver.exe + + - name: Packaging results as tar.gz + shell: pwsh + run: ./packaging.ps1 + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: Build Result + path: EXILED/bin/Release/Exiled.tar.gz + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c842e3a39..576a58f46b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,7 +13,7 @@ defaults: working-directory: ./EXILED env: - EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Master.zip + EXILED_REFERENCES_URL: https://exslmod-team.github.io/SL-References/Master.zip EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References jobs: diff --git a/.github/workflows/push_nuget.yml b/.github/workflows/push_nuget.yml index 5150b0c7ec..35bf02b145 100644 --- a/.github/workflows/push_nuget.yml +++ b/.github/workflows/push_nuget.yml @@ -10,7 +10,7 @@ defaults: working-directory: ./EXILED env: - EXILED_REFERENCES_URL: https://ExMod-Team.github.io/SL-References/Dev.zip + EXILED_REFERENCES_URL: https://exslmod-team.github.io/SL-References/Dev.zip EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac0937bd69..df466d0209 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,9 +11,9 @@ defaults: working-directory: ./EXILED env: - EXILED_REFERENCES_URL: https://exmod-team.github.io/SL-References/Dev.zip + EXILED_REFERENCES_URL: https://exslmod-team.github.io/SL-References/Dev.zip EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References - EXILED_DLL_ARCHIVER_URL: https://github.com/ExMod-Team/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe + EXILED_DLL_ARCHIVER_URL: https://github.com/ExSLMod-Team/EXILED-DLL-Archiver/releases/latest/download/EXILED-DLL-Archiver.exe jobs: build: diff --git a/EXILED/EXILED.props b/EXILED/EXILED.props index 641692ba4d..eefc8a1140 100644 --- a/EXILED/EXILED.props +++ b/EXILED/EXILED.props @@ -15,7 +15,7 @@ - 9.5.1 + 9.6.0 false @@ -25,8 +25,8 @@ Copyright © $(Authors) 2020 - $([System.DateTime]::Now.ToString("yyyy")) Git - https://github.com/ExMod-Team/EXILED - https://github.com/ExMod-Team/EXILED + https://github.com/ExSLMod-Team/EXILED + https://github.com/ExSLMod-Team/EXILED CC-BY-SA-3.0 $(DefineConstants);PUBLIC_BETA diff --git a/EXILED/Exiled.API/Enums/AdminToyType.cs b/EXILED/Exiled.API/Enums/AdminToyType.cs index c7f937721a..0ad8bb43dd 100644 --- a/EXILED/Exiled.API/Enums/AdminToyType.cs +++ b/EXILED/Exiled.API/Enums/AdminToyType.cs @@ -32,5 +32,25 @@ public enum AdminToyType /// Speaker toy. /// Speaker, + + /// + /// Capybara toy. + /// + Capybara, + + /// + /// InvisibleInteractable toy. + /// + InvisibleInteractableToy, + + /// + /// Camera Object toy. + /// + CameraToy, + + /// + /// Text toy. + /// + TextToy, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/CameraType.cs b/EXILED/Exiled.API/Enums/CameraType.cs index 6248d2a245..8b064b045f 100644 --- a/EXILED/Exiled.API/Enums/CameraType.cs +++ b/EXILED/Exiled.API/Enums/CameraType.cs @@ -58,7 +58,9 @@ public enum CameraType Hcz106Recontainment, HczChkptEz, HczChkptHcz, + [System.Obsolete("This Camera no longer exist.")] HczHIDChamber, + [System.Obsolete("This Camera no longer exist.")] HczHIDHallway, Hcz939, HczArmory, @@ -119,7 +121,9 @@ public enum CameraType Hcz173Outside, Hcz173Stairs, Hcz173ContChamber, + [System.Obsolete("This Camera no longer exist.")] Hcz173Hallway, + [System.Obsolete("This Camera no longer exist.")] HczCurve, HczJunkMain, HczJunkHallway, @@ -134,6 +138,19 @@ public enum CameraType HczWarheadPortElevator, HczMicroHIDLab, HczPipesMain, + HczScp127Lab, + HczScp127Containment, + HczServersUpperStorage, + HczLowerServerStorage, + HczServerStaircase, + #endregion + + #region custom + EzArmCameraToy, + EzCameraToy, + HczCameraToy, + LczCameraToy, + SzCameraToy, #endregion } } diff --git a/EXILED/Exiled.API/Enums/DamageType.cs b/EXILED/Exiled.API/Enums/DamageType.cs index 602ad2f740..ae93c5c020 100644 --- a/EXILED/Exiled.API/Enums/DamageType.cs +++ b/EXILED/Exiled.API/Enums/DamageType.cs @@ -269,5 +269,10 @@ public enum DamageType /// Damage caused by . /// SnowBall, + + /// + /// Damage caused by . + /// + Scp127, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/DoorType.cs b/EXILED/Exiled.API/Enums/DoorType.cs index c3e0221721..4e3cb644f5 100644 --- a/EXILED/Exiled.API/Enums/DoorType.cs +++ b/EXILED/Exiled.API/Enums/DoorType.cs @@ -167,11 +167,20 @@ public enum DoorType /// /// Represents the HID_UPPER door. /// + [Obsolete("This Door has been renamed too HID_LAB.")] HIDUpper, + /// + /// Represents the HID_LAB door. + /// +#pragma warning disable CS0618 + HIDLab = HIDUpper, +#pragma warning restore CS0618 + /// /// Represents the HID_LOWER door. /// + [Obsolete("This Door has been removed from the game.")] HIDLower, /// @@ -241,8 +250,9 @@ public enum DoorType CheckpointGateB, /// - /// Represents the Gate in the Checkpoint between EZ and HCZ. + /// Represents a door than Yamato never implemented. /// + [Obsolete("This Door has never been in the game.")] SurfaceDoor, /// @@ -319,5 +329,15 @@ public enum DoorType /// Represents the ESCAPE_FINAL door. /// EscapeFinal, + + /// + /// Represents the Elevator door for . + /// + ElevatorServerRoom, + + /// + /// Represents the HCZ_127_LAB door. + /// + Hcz127Lab, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/EffectType.cs b/EXILED/Exiled.API/Enums/EffectType.cs index 8ac82586aa..b32ba684d8 100644 --- a/EXILED/Exiled.API/Enums/EffectType.cs +++ b/EXILED/Exiled.API/Enums/EffectType.cs @@ -283,5 +283,10 @@ public enum EffectType /// [Obsolete("Only availaible for Christmas and AprilFools.")] Snowed, + + /// + /// . + /// + Scp1344Detected, } } diff --git a/EXILED/Exiled.API/Enums/ElevatorType.cs b/EXILED/Exiled.API/Enums/ElevatorType.cs index 02783b7180..ebccf5b356 100644 --- a/EXILED/Exiled.API/Enums/ElevatorType.cs +++ b/EXILED/Exiled.API/Enums/ElevatorType.cs @@ -48,5 +48,10 @@ public enum ElevatorType : byte /// Light Containment Zone checkpoint B elevator. /// LczB, + + /// + /// Heavy Containment Zone ServerRoom elevator. + /// + ServerRoom, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/FirearmType.cs b/EXILED/Exiled.API/Enums/FirearmType.cs index aa814136fa..05cccf2388 100644 --- a/EXILED/Exiled.API/Enums/FirearmType.cs +++ b/EXILED/Exiled.API/Enums/FirearmType.cs @@ -89,5 +89,10 @@ public enum FirearmType /// Represents the . /// A7, + + /// + /// Represents the . + /// + Scp127, } } diff --git a/EXILED/Exiled.API/Enums/GeneratorState.cs b/EXILED/Exiled.API/Enums/GeneratorState.cs index 1b434908b0..4f0a9b7111 100644 --- a/EXILED/Exiled.API/Enums/GeneratorState.cs +++ b/EXILED/Exiled.API/Enums/GeneratorState.cs @@ -20,9 +20,9 @@ namespace Exiled.API.Enums public enum GeneratorState : byte { /// - /// Generator is locked. + /// Generator is doing nothing. /// - None = 1, + None = 0, /// /// Generator is unlocked. diff --git a/EXILED/Exiled.API/Enums/GlassType.cs b/EXILED/Exiled.API/Enums/GlassType.cs index 9857ed2a82..4144c7fd98 100644 --- a/EXILED/Exiled.API/Enums/GlassType.cs +++ b/EXILED/Exiled.API/Enums/GlassType.cs @@ -69,5 +69,10 @@ public enum GlassType /// Represents the window in . /// TestRoom, + + /// + /// Represents the window in . + /// + Scp127, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/LockerType.cs b/EXILED/Exiled.API/Enums/LockerType.cs index b38b592c0a..14c2f365df 100644 --- a/EXILED/Exiled.API/Enums/LockerType.cs +++ b/EXILED/Exiled.API/Enums/LockerType.cs @@ -46,9 +46,15 @@ public enum LockerType Adrenaline, /// - /// Unknow type of locker. + /// Unknown type of locker. /// - Unknow, + Unknown, + + /// + /// Unknown type of locker. + /// + [Obsolete("Use LockerType.Unknown", true)] + Unknow = Unknown, /// /// MircoHid pedestal. @@ -109,5 +115,10 @@ public enum LockerType /// SCP-1853 pedestal. /// Scp1853Pedestal, + + /// + /// SCP-127 pedestal. + /// + Scp127Pedestal, } } diff --git a/EXILED/Exiled.API/Enums/MessageType.cs b/EXILED/Exiled.API/Enums/MessageType.cs new file mode 100644 index 0000000000..5c1454b791 --- /dev/null +++ b/EXILED/Exiled.API/Enums/MessageType.cs @@ -0,0 +1,27 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Enums +{ + using Exiled.API.Features; + + /// + /// List of types. + /// + public enum MessageType + { + /// + /// Broadcast. + /// + Broadcast, + + /// + /// Hint. + /// + Hint, + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/PrefabType.cs b/EXILED/Exiled.API/Enums/PrefabType.cs index 7ec8a851c3..f34093aba8 100644 --- a/EXILED/Exiled.API/Enums/PrefabType.cs +++ b/EXILED/Exiled.API/Enums/PrefabType.cs @@ -336,5 +336,41 @@ public enum PrefabType [Prefab(912031041, "ElevatorChamberNuke")] ElevatorChamberNuke, + + [Prefab(3087007600, "CapybaraToy")] + CapybaraToy, + + [Prefab(3539746802, "Sinkhole")] + Sinkhole, + + [Prefab(1548138668, "AutoRagdoll")] + AutoRagdoll, + + [Prefab(1323017091, "ElevatorChamberCargo")] + ElevatorChamberCargo, + + [Prefab(359728307, "InvisibleInteractableToy")] + InvisibleInteractableToy, + + [Prefab(1824808402, "EzArmCameraToy")] + EzArmCameraToy, + + [Prefab(3375932423, "EzCameraToy")] + EzCameraToy, + + [Prefab(144958943, "HczCameraToy")] + HczCameraToy, + + [Prefab(2026969629, "LczCameraToy")] + LczCameraToy, + + [Prefab(1548138668, "SzCameraToy")] + SzCameraToy, + + [Prefab(2842703865, "KeycardPickup_Chaos")] + KeycardPickupChaos, + + [Prefab(162530276, "TextToy")] + TextToy, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Enums/RoomType.cs b/EXILED/Exiled.API/Enums/RoomType.cs index 3cb78e53f2..6e236c25d1 100644 --- a/EXILED/Exiled.API/Enums/RoomType.cs +++ b/EXILED/Exiled.API/Enums/RoomType.cs @@ -328,5 +328,15 @@ public enum RoomType /// Entrance Zone's straight hall with Dr.L's and conference room 9b locked room. /// EzSmallrooms, + + /// + /// Heavy Containment Zone's SCP-330 room. + /// + Hcz127, + + /// + /// Heavy Containment Zone's storage / server room. + /// + HczServerRoom, } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Exiled.API.csproj b/EXILED/Exiled.API/Exiled.API.csproj index 961b44e98d..c8737a790c 100644 --- a/EXILED/Exiled.API/Exiled.API.csproj +++ b/EXILED/Exiled.API/Exiled.API.csproj @@ -27,10 +27,10 @@ + - diff --git a/EXILED/Exiled.API/Extensions/CommonExtensions.cs b/EXILED/Exiled.API/Extensions/CommonExtensions.cs index b4db94160a..6b175fc281 100644 --- a/EXILED/Exiled.API/Extensions/CommonExtensions.cs +++ b/EXILED/Exiled.API/Extensions/CommonExtensions.cs @@ -56,9 +56,11 @@ public static T GetRandomValue(this IEnumerable enumerable, System.FuncThe 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; + Keyframe[] keys = curve.keys; + for (int i = 0; i < keys.Length; i++) + keys[i].value *= amount; + curve.keys = keys; return curve; } @@ -70,9 +72,11 @@ public static AnimationCurve Multiply(this AnimationCurve curve, float amount) /// 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; + Keyframe[] keys = curve.keys; + for (int i = 0; i < keys.Length; i++) + keys[i].value += amount; + curve.keys = keys; return curve; } } diff --git a/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs b/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs index 7e496e77ac..6abeea8a2f 100644 --- a/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs +++ b/EXILED/Exiled.API/Extensions/DamageTypeExtensions.cs @@ -102,6 +102,7 @@ public static class DamageTypeExtensions { ItemType.Jailbird, DamageType.Jailbird }, { ItemType.GunFRMG0, DamageType.Frmg0 }, { ItemType.GunA7, DamageType.A7 }, + { ItemType.GunSCP127, DamageType.Scp127 }, }; /// diff --git a/EXILED/Exiled.API/Extensions/DoorTypeExtensions.cs b/EXILED/Exiled.API/Extensions/DoorTypeExtensions.cs index 79164df2b3..a4380d81cf 100644 --- a/EXILED/Exiled.API/Extensions/DoorTypeExtensions.cs +++ b/EXILED/Exiled.API/Extensions/DoorTypeExtensions.cs @@ -35,6 +35,6 @@ public static bool IsGate(this DoorType door) => door is DoorType.GateA or DoorT /// The door to be checked. /// Returns whether the is an elevator. public static bool IsElevator(this DoorType door) => door is DoorType.ElevatorGateA or DoorType.ElevatorGateB - or DoorType.ElevatorLczA or DoorType.ElevatorLczB or DoorType.ElevatorNuke or DoorType.ElevatorScp049; + or DoorType.ElevatorLczA or DoorType.ElevatorLczB or DoorType.ElevatorNuke or DoorType.ElevatorScp049 or DoorType.ElevatorServerRoom; } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs b/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs index dc209c3ba0..6f038fd761 100644 --- a/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs +++ b/EXILED/Exiled.API/Extensions/EffectTypeExtension.cs @@ -75,6 +75,7 @@ public static class EffectTypeExtension { EffectType.SeveredEyes, typeof(SeveredEyes) }, { EffectType.PitDeath, typeof(PitDeath) }, { EffectType.Blurred, typeof(Blurred) }, + { EffectType.Scp1344Detected, typeof(Scp1344Detected) }, #pragma warning disable CS0618 { EffectType.Marshmallow, typeof(MarshmallowEffect) }, { EffectType.BecomingFlamingo, typeof(BecomingFlamingo) }, diff --git a/EXILED/Exiled.API/Extensions/ItemExtensions.cs b/EXILED/Exiled.API/Extensions/ItemExtensions.cs index c169f03011..458fd10bc4 100644 --- a/EXILED/Exiled.API/Extensions/ItemExtensions.cs +++ b/EXILED/Exiled.API/Extensions/ItemExtensions.cs @@ -182,6 +182,7 @@ public static int GetMaxAmmo(this FirearmType item) ItemType.GunCom45 => FirearmType.Com45, ItemType.GunFRMG0 => FirearmType.FRMG0, ItemType.ParticleDisruptor => FirearmType.ParticleDisruptor, + ItemType.GunSCP127 => FirearmType.Scp127, _ => FirearmType.None, }; @@ -220,6 +221,7 @@ public static int GetMaxAmmo(this FirearmType item) FirearmType.Com45 => ItemType.GunCom45, FirearmType.FRMG0 => ItemType.GunFRMG0, FirearmType.ParticleDisruptor => ItemType.ParticleDisruptor, + FirearmType.Scp127 => ItemType.GunSCP127, _ => ItemType.None, }; diff --git a/EXILED/Exiled.API/Extensions/LockerExtensions.cs b/EXILED/Exiled.API/Extensions/LockerExtensions.cs index bdf3689bbc..da8b915794 100644 --- a/EXILED/Exiled.API/Extensions/LockerExtensions.cs +++ b/EXILED/Exiled.API/Extensions/LockerExtensions.cs @@ -48,7 +48,8 @@ public static class LockerExtensions "RegularMedkitStructure" => LockerType.Medkit, "AdrenalineMedkitStructure" => LockerType.Adrenaline, "MicroHIDpedestal" => LockerType.MicroHid, - _ => LockerType.Unknow, + "SCP_127_Container" => LockerType.Scp127Pedestal, + _ => LockerType.Unknown, }; } } diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs index cf0a978724..3244663a3e 100644 --- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs +++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs @@ -15,21 +15,24 @@ namespace Exiled.API.Extensions using System.Reflection.Emit; using System.Text; + using AudioPooling; using Exiled.API.Enums; + using Exiled.API.Features.Items; using Features; using Features.Pools; - + using InventorySystem; + using InventorySystem.Items; + using InventorySystem.Items.Firearms; + using InventorySystem.Items.Firearms.Modules; + using MEC; using Mirror; - using PlayerRoles; using PlayerRoles.FirstPersonControl; using PlayerRoles.PlayableScps.Scp049.Zombies; using PlayerRoles.PlayableScps.Scp1507; using PlayerRoles.Voice; using RelativePositioning; - using Respawning; - using UnityEngine; /// @@ -168,20 +171,53 @@ public static ReadOnlyDictionary RpcFullNames /// Weapon' sound to play. /// Sound's volume to set. /// GunAudioMessage's audioClipId to set (default = 0). + [Obsolete("This method is not working. Use PlayGunSound(Player, Vector3, FirearmType, float, int, bool) overload instead.")] public static void PlayGunSound(this Player player, Vector3 position, ItemType itemType, byte volume, byte audioClipId = 0) + => PlayGunSound(player, position, itemType.GetFirearmType(), volume, audioClipId); + + /// + /// Plays a gun sound that only the can hear. + /// + /// Target to play. + /// Position to play on. + /// Weapon's sound to play. + /// Speed of sound. + /// Index of clip. + public static void PlayGunSound(this Player player, Vector3 position, FirearmType firearmType, float pitch = 1, int clipIndex = 0) { - // TODO: Not finish - /* - GunAudioMessage message = new() + if (firearmType is FirearmType.ParticleDisruptor or FirearmType.None) + return; + + Features.Items.Firearm firearm = Features.Items.Firearm.ItemTypeToFirearmInstance[firearmType]; + + if (firearm == null) + return; + + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { - Weapon = itemType, - AudioClipId = audioClipId, - MaxDistance = volume, - ShooterHub = player.ReferenceHub, - ShooterPosition = new RelativePosition(position), - }; + writer.WriteUShort(NetworkMessageId.Id); + new RoleSyncInfo(Server.Host.ReferenceHub, RoleTypeId.ClassD, player.ReferenceHub).Write(writer); + writer.WriteRelativePosition(new RelativePosition(0, 0, 0, 0, false)); + writer.WriteUShort(0); + player.Connection.Send(writer); + } + + firearm.BarrelAmmo = 1; + firearm.BarrelMagazine.IsCocked = true; + player.SendFakeSyncVar(Server.Host.Inventory.netIdentity, typeof(Inventory), nameof(Inventory.NetworkCurItem), firearm.Identifier); - player.Connection.Send(message);*/ + if (!firearm.Base.TryGetModule(out AudioModule audioModule)) + return; + + Timing.CallDelayed(0.1f, () => // due to selecting item we need to delay shot a bit + { + audioModule.SendRpc(player.ReferenceHub, writer => + audioModule.ServerSend(writer, clipIndex, pitch, MixerChannel.Weapons, 12f, position, false)); + + player.SendFakeSyncVar(Server.Host.Inventory.netIdentity, typeof(Inventory), nameof(Inventory.NetworkCurItem), ItemIdentifier.None); + + player.Connection.Send(new RoleSyncInfo(Server.Host.ReferenceHub, Server.Host.Role, player.ReferenceHub)); + }); } /// diff --git a/EXILED/Exiled.API/Extensions/RoomExtensions.cs b/EXILED/Exiled.API/Extensions/RoomExtensions.cs index 14141a4422..8d36486505 100644 --- a/EXILED/Exiled.API/Extensions/RoomExtensions.cs +++ b/EXILED/Exiled.API/Extensions/RoomExtensions.cs @@ -52,7 +52,7 @@ public static bool IsCheckpoint(this RoomType room) => room is RoomType.LczCheck /// Returns whether the contains any SCP. public static bool IsScp(this RoomType room) => room is RoomType.Lcz173 or RoomType.Lcz330 or RoomType.Lcz914 or RoomType.Hcz049 or RoomType.Hcz079 or - RoomType.Hcz096 or RoomType.Hcz106 or RoomType.Hcz939; + RoomType.Hcz096 or RoomType.Hcz106 or RoomType.Hcz939 or RoomType.Hcz127; /// /// Converts the provided into the corresponding . diff --git a/EXILED/Exiled.API/Extensions/UserGroupExtensions.cs b/EXILED/Exiled.API/Extensions/UserGroupExtensions.cs index 7b9138618c..41b8523433 100644 --- a/EXILED/Exiled.API/Extensions/UserGroupExtensions.cs +++ b/EXILED/Exiled.API/Extensions/UserGroupExtensions.cs @@ -37,7 +37,7 @@ public static bool EqualsTo(this UserGroup @this, UserGroup other) /// /// The . /// The key of that group, or if not found. - public static string GetKey(this UserGroup @this) => Server.PermissionsHandler._groups + public static string GetKey(this UserGroup @this) => Server.PermissionsHandler.Groups .FirstOrDefault(pair => pair.Value.EqualsTo(@this)).Key; /// @@ -47,7 +47,7 @@ public static string GetKey(this UserGroup @this) => Server.PermissionsHandler._ /// The value of that group, or if not found. public static UserGroup GetValue(string groupName) { - ServerStatic.GetPermissionsHandler().GetAllGroups().TryGetValue(groupName, out UserGroup userGroup); + ServerStatic.PermissionsHandler.GetAllGroups().TryGetValue(groupName, out UserGroup userGroup); return userGroup; } } diff --git a/EXILED/Exiled.API/Features/Camera.cs b/EXILED/Exiled.API/Features/Camera.cs index 6c72485739..da3f73e27b 100644 --- a/EXILED/Exiled.API/Features/Camera.cs +++ b/EXILED/Exiled.API/Features/Camera.cs @@ -66,13 +66,10 @@ public class Camera : IWrapper, IWorldSpace ["106 RECONTAINMENT"] = CameraType.Hcz106Recontainment, ["CHKPT (EZ)"] = CameraType.HczChkptEz, ["CHKPT (HCZ)"] = CameraType.HczChkptHcz, - ["H.I.D. CHAMBER"] = CameraType.HczHIDChamber, - ["H.I.D. HALLWAY"] = CameraType.HczHIDHallway, ["HCZ 939"] = CameraType.Hcz939, ["HCZ ARMORY"] = CameraType.HczArmory, ["HCZ ARMORY INTERIOR"] = CameraType.HczArmoryInterior, ["HCZ CROSSING"] = CameraType.HczCrossing, - ["HCZ CURVE"] = CameraType.HczCurve, ["HCZ ELEV SYS A"] = CameraType.HczElevSysA, ["HCZ ELEV SYS B"] = CameraType.HczElevSysB, ["HCZ HALLWAY"] = CameraType.HczHallway, @@ -136,6 +133,18 @@ public class Camera : IWrapper, IWorldSpace ["WARHEAD TOP ELEVATORS"] = CameraType.HczWarheadTopElevators, ["WARHEAD CONNECTOR"] = CameraType.HczWarheadConnector, ["WARHEAD PORT ELEVATOR"] = CameraType.HczWarheadPortElevator, + ["HCZ SCP-127 LAB"] = CameraType.HczScp127Lab, + ["HCZ SCP-127 CONTAINMENT"] = CameraType.HczScp127Containment, + ["HCZ SERVERS UPPER STORAGE"] = CameraType.HczServersUpperStorage, + ["HCZ LOWER SERVER STORAGE"] = CameraType.HczLowerServerStorage, + ["HCZ SERVERS STAIRCASE"] = CameraType.HczServerStaircase, + + // CustomCamera + ["EZ ARM CAMERA TOY"] = CameraType.EzArmCameraToy, + ["EZ CAMERA TOY"] = CameraType.EzCameraToy, + ["HCZ CAMERA TOY"] = CameraType.HczCameraToy, + ["LCZ CAMERA TOY"] = CameraType.LczCameraToy, + ["SZ CAMERA TOY"] = CameraType.SzCameraToy, }; private Room room; @@ -149,10 +158,6 @@ internal Camera(Scp079Camera camera079) Base = camera079; Camera079ToCamera.Add(camera079, this); Type = GetCameraType(); -#if DEBUG - if (Type is CameraType.Unknown) - Log.Error($"[CAMERATYPE UNKNOWN] {this} BASE = {Base}"); -#endif } /// @@ -216,8 +221,8 @@ internal Camera(Scp079Camera camera079) /// public Quaternion Rotation { - get => Base._cameraAnchor.rotation; - set => Base._cameraAnchor.rotation = value; + get => Base.CameraAnchor.rotation; + set => Base.CameraAnchor.rotation = value; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs b/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs index b27ae1fc78..2cbc85e645 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs @@ -259,13 +259,43 @@ public static IEnumerable Register(IEnumerable setting return result; } + /// + /// Registers all settings from the specified collection to player. + /// + /// A player that will receive settings. + /// A collection of settings to register. + /// A of instances that were successfully registered. + /// This method is used to sync new settings with players. + public static IEnumerable Register(Player player, IEnumerable settings) + { + IEnumerable> grouped = settings.Where(s => s != null).GroupBy(s => s.Header); + + List result = new(); + + // Group settings by headers + foreach (IGrouping grouping in grouped) + { + if (grouping.Key != null) + result.Add(grouping.Key); + + result.AddRange(grouping); + } + + ServerSpecificSettingsSync.DefinedSettings = (ServerSpecificSettingsSync.DefinedSettings ?? Array.Empty()).Concat(result.Select(s => s.Base)).ToArray(); + Settings.AddRange(result); + + SendToPlayer(player); + + return result; + } + /// /// Removes settings from players. /// /// Determines which players will receive this update. /// Settings to remove. If null, all settings will be removed. /// A of instances that were successfully removed. - /// This method is used to unsync settings from players. Using it with provides an opportunity to update synced settings. + /// This method is used to unsync settings from players. Using it with provides an opportunity to update synced settings. public static IEnumerable Unregister(Func predicate = null, IEnumerable settings = null) { List list = ListPool.Pool.Get(ServerSpecificSettingsSync.DefinedSettings); @@ -283,6 +313,27 @@ public static IEnumerable Unregister(Func predicate = return list2; } + /// + /// Removes settings from players. + /// + /// Determines which player will receive this update. + /// Settings to remove. If null, all settings will be removed. + /// A of instances that were successfully removed. + /// This method is used to unsync settings from players. Using it with provides an opportunity to update synced settings. + public static IEnumerable Unregister(Player player, IEnumerable settings = null) + { + List list = ListPool.Pool.Get(ServerSpecificSettingsSync.DefinedSettings); + List list2 = new((settings ?? Settings).Where(setting => list.Remove(setting.Base))); + + ServerSpecificSettingsSync.DefinedSettings = list.ToArray(); + + SendToPlayer(player); + + ListPool.Pool.Return(list); + + return list2; + } + /// /// Returns a string representation of this . /// diff --git a/EXILED/Exiled.API/Features/CustomHealthStat.cs b/EXILED/Exiled.API/Features/CustomHealthStat.cs deleted file mode 100644 index 57f6d31c20..0000000000 --- a/EXILED/Exiled.API/Features/CustomHealthStat.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.API.Features -{ - using PlayerStatsSystem; - - /// - /// A custom version of which allows the player's max amount of health to be changed. - /// TODO: Move to Features.CustomStats. - /// - public class CustomHealthStat : HealthStat - { - /// - public override float MaxValue => CustomMaxValue == default ? base.MaxValue : CustomMaxValue; - - /// - /// Gets or sets the maximum amount of health the player will have. - /// - public float CustomMaxValue { get; set; } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs b/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs index 78c4cd807d..e19ce970f5 100644 --- a/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs +++ b/EXILED/Exiled.API/Features/CustomStats/CustomHumeShieldStat.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. @@ -18,25 +18,24 @@ namespace Exiled.API.Features.CustomStats /// public class CustomHumeShieldStat : HumeShieldStat { - /// - public override float MaxValue => CustomMaxValue == -1 ? base.MaxValue : CustomMaxValue; - /// /// Gets or sets the multiplier for gaining HumeShield. /// public float ShieldRegenerationMultiplier { get; set; } = 1; - /// - /// Gets or sets the maximum amount of HumeShield the player can have. - /// - public float CustomMaxValue { get; set; } = -1; - - private float ShieldRegeneration => TryGetHsModule(out HumeShieldModuleBase controller) ? controller.HsRegeneration * ShieldRegenerationMultiplier : 0; + private float ShieldRegeneration + { + get + { + IHumeShieldProvider.GetForHub(Hub, out _, out _, out float hsRegen, out _); + return hsRegen * ShieldRegenerationMultiplier; + } + } /// public override void Update() { - if (MaxValue == -1 && ShieldRegenerationMultiplier is 1) + if (ShieldRegenerationMultiplier is 1) { base.Update(); return; @@ -45,7 +44,7 @@ public override void Update() if (!NetworkServer.active) return; - if (_valueDirty) + if (ValueDirty) { new SyncedStatMessages.StatMessage() { @@ -53,7 +52,7 @@ public override void Update() SyncedValue = CurValue, }.SendToHubsConditionally(CanReceive); _lastSent = CurValue; - _valueDirty = false; + ValueDirty = false; } if (ShieldRegeneration == 0) diff --git a/EXILED/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs b/EXILED/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs index 2008936bea..bd858b3447 100644 --- a/EXILED/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs +++ b/EXILED/Exiled.API/Features/DamageHandlers/GenericDamageHandler.cs @@ -155,6 +155,9 @@ public GenericDamageHandler(Player player, Player attacker, float damage, Damage case DamageType.A7: GenericFirearm(player, attacker, damage, damageType, ItemType.GunA7); break; + case DamageType.Scp127: + GenericFirearm(player, attacker, damage, damageType, ItemType.GunSCP127); + break; case DamageType.ParticleDisruptor: Base = new DisruptorDamageHandler(new (Item.Create(ItemType.ParticleDisruptor, attacker).Base as InventorySystem.Items.Firearms.Firearm, InventorySystem.Items.Firearms.Modules.DisruptorActionModule.FiringState.FiringSingle), Vector3.up, damage); break; diff --git a/EXILED/Exiled.API/Features/Doors/BasicDoor.cs b/EXILED/Exiled.API/Features/Doors/BasicDoor.cs index 39316fdd37..5a53caa738 100644 --- a/EXILED/Exiled.API/Features/Doors/BasicDoor.cs +++ b/EXILED/Exiled.API/Features/Doors/BasicDoor.cs @@ -9,6 +9,8 @@ namespace Exiled.API.Features.Doors { using System.Collections.Generic; + using Exiled.API.Enums; + using PlayerRoles; using UnityEngine; using Basegame = Interactables.Interobjects.BasicDoor; @@ -35,9 +37,9 @@ public BasicDoor(Basegame door, List room) public new Basegame Base { get; } /// - /// Gets the list with all SCP-106's colliders. + /// Gets the list with all Ignored's colliders for or . /// - public IEnumerable Scp106Colliders => Base.Scp106Colliders; + public IEnumerable Scp106Colliders => Base.IgnoredColliders; /// /// Gets or sets the total cooldown before door can be triggered again. diff --git a/EXILED/Exiled.API/Features/Doors/CheckpointDoor.cs b/EXILED/Exiled.API/Features/Doors/CheckpointDoor.cs index 837bd5ff72..0fa6ea4139 100644 --- a/EXILED/Exiled.API/Features/Doors/CheckpointDoor.cs +++ b/EXILED/Exiled.API/Features/Doors/CheckpointDoor.cs @@ -42,10 +42,10 @@ internal CheckpointDoor(Interactables.Interobjects.CheckpointDoor door, List /// Gets or sets the current checkpoint stage. /// - public Interactables.Interobjects.CheckpointDoor.CheckpointSequenceStage CurrentStage + public Interactables.Interobjects.CheckpointDoor.SequenceState CurrentStage { - get => Base._currentSequence; - set => Base._currentSequence = value; + get => Base.CurSequence; + set => Base.CurSequence = value; } /// @@ -53,8 +53,8 @@ public Interactables.Interobjects.CheckpointDoor.CheckpointSequenceStage Current /// public float MainTimer { - get => Base._mainTimer; - set => Base._mainTimer = value; + get => Base.SequenceCtrl.RemainingTime; + set => Base.SequenceCtrl.RemainingTime = value; } /// @@ -62,8 +62,8 @@ public float MainTimer /// public float WaitTime { - get => Base._waitTime; - set => Base._waitTime = value; + get => Base.SequenceCtrl.OpenLoopTime; + set => Base.SequenceCtrl.OpenLoopTime = value; } /// @@ -71,8 +71,8 @@ public float WaitTime /// public float WarningTime { - get => Base._warningTime; - set => Base._warningTime = value; + get => Base.SequenceCtrl.WarningTime; + set => Base.SequenceCtrl.WarningTime = value; } /// diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs index fb947039dc..c2cea9a761 100644 --- a/EXILED/Exiled.API/Features/Doors/Door.cs +++ b/EXILED/Exiled.API/Features/Doors/Door.cs @@ -22,11 +22,10 @@ namespace Exiled.API.Features.Doors using UnityEngine; using BaseBreakableDoor = Interactables.Interobjects.BreakableDoor; - using BaseKeycardPermissions = Interactables.Interobjects.DoorUtils.KeycardPermissions; using Breakable = BreakableDoor; using Checkpoint = CheckpointDoor; using Elevator = ElevatorDoor; - using KeycardPermissions = Enums.KeycardPermissions; + using KeycardPermissions = Exiled.API.Enums.KeycardPermissions; /// /// A wrapper class for . @@ -55,10 +54,6 @@ internal Door(DoorVariant door, List rooms) } Type = GetDoorType(); -#if DEBUG - if (Type is DoorType.UnknownDoor or DoorType.UnknownGate or DoorType.UnknownElevator) - Log.Error($"[DOORTYPE UNKNOWN] {this} BASE = {Base}"); -#endif } /// @@ -181,8 +176,8 @@ public bool IsOpen /// public KeycardPermissions KeycardPermissions { - get => (KeycardPermissions)RequiredPermissions.RequiredPermissions; - set => RequiredPermissions.RequiredPermissions = (BaseKeycardPermissions)value; + get => (KeycardPermissions)RequiredPermissions; + set => RequiredPermissions = (DoorPermissionFlags)value; } /// @@ -244,10 +239,10 @@ public DoorLockType DoorLockType /// /// Gets or sets the required permissions to open the door. /// - public DoorPermissions RequiredPermissions + public DoorPermissionFlags RequiredPermissions { - get => Base.RequiredPermissions; - set => Base.RequiredPermissions = value; + get => Base.RequiredPermissions.RequiredPermissions; + set => Base.RequiredPermissions.RequiredPermissions = value; } /// @@ -357,7 +352,7 @@ public static T Get(string name) /// /// The base-game . /// The with the given name or if not found. - public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInChildren()); + public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInParent()); /// /// Returns the closest to the given . @@ -474,11 +469,11 @@ public void PlaySound(DoorBeepType beep) { switch (Base) { - case Interactables.Interobjects.BasicDoor basic: - basic.RpcPlayBeepSound(beep is not DoorBeepType.InteractionAllowed); + case Interactables.Interobjects.BasicDoor basic when beep is not DoorBeepType.InteractionAllowed: + basic.RpcPlayBeepSound(); break; - case Interactables.Interobjects.CheckpointDoor chkPt: - chkPt.RpcPlayBeepSound((byte)Mathf.Min((int)beep, 3)); + case Interactables.Interobjects.CheckpointDoor chkPt when beep is not DoorBeepType.InteractionAllowed: + chkPt.RpcPlayDeniedBeep(); break; } } @@ -553,7 +548,7 @@ public void Lock(DoorLockType lockType) /// Returns the Door in a human-readable format. /// /// A string containing Door-related data. - public override string ToString() => $"{Type} ({Zone}) [{Room}] *{DoorLockType}* ={RequiredPermissions?.RequiredPermissions}="; + public override string ToString() => $"{Type} ({Zone}) [{Room}] *{DoorLockType}* ={KeycardPermissions}="; /// /// Creates the door object associated with a specific . @@ -613,6 +608,7 @@ private DoorType GetDoorType() ElevatorGroup.Scp049 => DoorType.ElevatorScp049, ElevatorGroup.GateB => DoorType.ElevatorGateB, ElevatorGroup.GateA => DoorType.ElevatorGateA, + ElevatorGroup.ServerRoom => DoorType.ElevatorServerRoom, ElevatorGroup.LczA01 or ElevatorGroup.LczA02 => DoorType.ElevatorLczA, ElevatorGroup.LczB01 or ElevatorGroup.LczB02 => DoorType.ElevatorLczB, ElevatorGroup.Nuke01 or ElevatorGroup.Nuke02 => DoorType.ElevatorNuke, @@ -654,8 +650,8 @@ private DoorType GetDoorType() "173_CONNECTOR" => DoorType.Scp173Connector, "LCZ_WC" => DoorType.LczWc, "HID_CHAMBER" => DoorType.HIDChamber, - "HID_UPPER" => DoorType.HIDUpper, - "HID_LOWER" => DoorType.HIDLower, + "HID_LAB" => DoorType.HIDLab, + "HCZ_127_LAB" => DoorType.Hcz127Lab, "173_ARMORY" => DoorType.Scp173Armory, "173_GATE" => DoorType.Scp173Gate, "GR18" => DoorType.GR18Gate, diff --git a/EXILED/Exiled.API/Features/Doors/ElevatorDoor.cs b/EXILED/Exiled.API/Features/Doors/ElevatorDoor.cs index 797378312a..0145e53f84 100644 --- a/EXILED/Exiled.API/Features/Doors/ElevatorDoor.cs +++ b/EXILED/Exiled.API/Features/Doors/ElevatorDoor.cs @@ -30,7 +30,7 @@ internal ElevatorDoor(Interactables.Interobjects.ElevatorDoor door, List r Base = door; Lift = Lift.Get(x => x.Group == Group).FirstOrDefault(); - Panel = Object.FindObjectsOfType().FirstOrDefault(x => x._door == door); + Panel = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None).FirstOrDefault(x => x._door == door); } /// @@ -56,6 +56,7 @@ internal ElevatorDoor(Interactables.Interobjects.ElevatorDoor door, List r ElevatorGroup.Scp049 => ElevatorType.Scp049, ElevatorGroup.GateA => ElevatorType.GateA, ElevatorGroup.GateB => ElevatorType.GateB, + ElevatorGroup.ServerRoom => ElevatorType.ServerRoom, ElevatorGroup.LczA01 or ElevatorGroup.LczA02 => ElevatorType.LczA, ElevatorGroup.LczB01 or ElevatorGroup.LczB02 => ElevatorType.LczB, ElevatorGroup.Nuke01 or ElevatorGroup.Nuke02 => ElevatorType.Nuke, diff --git a/EXILED/Exiled.API/Features/Generator.cs b/EXILED/Exiled.API/Features/Generator.cs index 7b8a6b6baf..b19369208d 100644 --- a/EXILED/Exiled.API/Features/Generator.cs +++ b/EXILED/Exiled.API/Features/Generator.cs @@ -13,7 +13,7 @@ namespace Exiled.API.Features using Enums; using Exiled.API.Interfaces; - + using Interactables.Interobjects.DoorUtils; using MapGeneration.Distributors; using UnityEngine; @@ -216,7 +216,7 @@ public Player LastActivator public KeycardPermissions KeycardPermissions { get => (KeycardPermissions)Base._requiredPermission; - set => Base._requiredPermission = (Interactables.Interobjects.DoorUtils.KeycardPermissions)value; + set => Base._requiredPermission = (DoorPermissionFlags)value; } /// @@ -281,7 +281,13 @@ public static bool TryGet(Func predicate, out IEnumerable /// Denies the unlock. /// - public void DenyUnlock() => Base.RpcDenied(); + public void DenyUnlock() => Base.RpcDenied(DoorPermissionFlags.None); + + /// + /// Denies the unlock. + /// + /// . + public void DenyUnlock(KeycardPermissions doorPermission) => Base.RpcDenied((Interactables.Interobjects.DoorUtils.DoorPermissionFlags)doorPermission); /// /// Denies the unlock and resets the interaction cooldown. @@ -299,7 +305,7 @@ public void DenyUnlockAndResetCooldown() /// A value indicating whether the flag is enabled. public void SetPermissionFlag(KeycardPermissions flag, bool isEnabled) { - Interactables.Interobjects.DoorUtils.KeycardPermissions permission = (Interactables.Interobjects.DoorUtils.KeycardPermissions)flag; + DoorPermissionFlags permission = (DoorPermissionFlags)flag; if (isEnabled) Base._requiredPermission |= permission; diff --git a/EXILED/Exiled.API/Features/Hazards/TemporaryHazard.cs b/EXILED/Exiled.API/Features/Hazards/TemporaryHazard.cs index 0e701a8093..c9dfecb650 100644 --- a/EXILED/Exiled.API/Features/Hazards/TemporaryHazard.cs +++ b/EXILED/Exiled.API/Features/Hazards/TemporaryHazard.cs @@ -56,8 +56,8 @@ public bool IsDestroyed /// public float Duration { - get => Base._elapsed; - set => Base._elapsed = value; + get => Base.Elapsed; + set => Base.Elapsed = value; } /// diff --git a/EXILED/Exiled.API/Features/Items/Armor.cs b/EXILED/Exiled.API/Features/Items/Armor.cs index 238de555ec..344b7a0118 100644 --- a/EXILED/Exiled.API/Features/Items/Armor.cs +++ b/EXILED/Exiled.API/Features/Items/Armor.cs @@ -65,21 +65,22 @@ internal Armor(ItemType type) public bool IsWorn => Base.IsWorn; /// - /// Gets or sets the Weight of the armor. + /// Gets or sets a value indicating whether excess ammo should be removed when the armor is dropped. /// - public new float Weight + [Obsolete("Not functional anymore", true)] + public bool RemoveExcessOnDrop { - get => Base.Weight; - set => Base._weight = value; + get => false; + set => _ = value; } /// - /// Gets or sets a value indicating whether excess ammo should be removed when the armor is dropped. + /// Gets or sets the Weight of the armor. /// - public bool RemoveExcessOnDrop + public new float Weight { - get => !Base.DontRemoveExcessOnDrop; - set => Base.DontRemoveExcessOnDrop = !value; + get => Base.Weight; + set => Base._weight = value; } /// diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs index 710a031a1d..c329b614ff 100644 --- a/EXILED/Exiled.API/Features/Items/Item.cs +++ b/EXILED/Exiled.API/Features/Items/Item.cs @@ -195,6 +195,11 @@ public ItemAddReason AddReason set => Base.ServerAddReason = value; } + /// + /// Gets the for this item. + /// + public ItemIdentifier Identifier => Base.ItemId; + /// /// Gets an existing or creates a new instance of one. /// diff --git a/EXILED/Exiled.API/Features/Items/Jailbird.cs b/EXILED/Exiled.API/Features/Items/Jailbird.cs index 6d7ac7d32d..b68a3473c2 100644 --- a/EXILED/Exiled.API/Features/Items/Jailbird.cs +++ b/EXILED/Exiled.API/Features/Items/Jailbird.cs @@ -98,7 +98,11 @@ public float Radius public float TotalDamageDealt { get => Base._hitreg.TotalMeleeDamageDealt; - set => Base._hitreg.TotalMeleeDamageDealt = value; + set + { + Base._hitreg.TotalMeleeDamageDealt = value; + Base._deterioration.RecheckUsage(); + } } /// @@ -107,7 +111,11 @@ public float TotalDamageDealt public int TotalCharges { get => Base.TotalChargesPerformed; - set => Base.TotalChargesPerformed = value; + set + { + Base.TotalChargesPerformed = value; + Base._deterioration.RecheckUsage(); + } } /// diff --git a/EXILED/Exiled.API/Features/Items/Keycard.cs b/EXILED/Exiled.API/Features/Items/Keycard.cs index 0bd390018b..4e68dd60a5 100644 --- a/EXILED/Exiled.API/Features/Items/Keycard.cs +++ b/EXILED/Exiled.API/Features/Items/Keycard.cs @@ -7,14 +7,12 @@ namespace Exiled.API.Features.Items { + using System; + using Exiled.API.Enums; - using Exiled.API.Features.Pickups; using Exiled.API.Interfaces; - using InventorySystem.Items.Keycards; - using KeycardPickup = Pickups.KeycardPickup; - /// /// A wrapper class for . /// @@ -49,33 +47,30 @@ internal Keycard(ItemType type) /// public KeycardPermissions Permissions { - get => (KeycardPermissions)Base.Permissions; - set => Base.Permissions = (Interactables.Interobjects.DoorUtils.KeycardPermissions)value; - } + get + { + foreach (DetailBase detail in Base.Details) + { + switch (detail) + { + case PredefinedPermsDetail predefinedPermsDetail: + return (KeycardPermissions)predefinedPermsDetail.Levels.Permissions; + case CustomPermsDetail customPermsDetail: + return (KeycardPermissions)customPermsDetail.GetPermissions(null); + } + } - /// - /// Clones current object. - /// - /// New object. - public override Item Clone() => new Keycard(Type) - { - Permissions = Permissions, - }; + return KeycardPermissions.None; + } + + [Obsolete("Not functional anymore", true)] + set => _ = value; + } /// /// Returns the Keycard in a human readable format. /// /// A string containing Keycard-related data. public override string ToString() => $"{Type} ({Serial}) [{Weight}] *{Scale}* |{Permissions}|"; - - /// - internal override void ReadPickupInfoBefore(Pickup pickup) - { - base.ReadPickupInfoBefore(pickup); - if (pickup is KeycardPickup keycardPickup) - { - Permissions = keycardPickup.Permissions; - } - } } } \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Items/Scp1344.cs b/EXILED/Exiled.API/Features/Items/Scp1344.cs index 460aeb8c06..7f96da8903 100644 --- a/EXILED/Exiled.API/Features/Items/Scp1344.cs +++ b/EXILED/Exiled.API/Features/Items/Scp1344.cs @@ -11,7 +11,7 @@ namespace Exiled.API.Features.Items using InventorySystem.Items.Usables; using InventorySystem.Items.Usables.Scp1344; - using PlayerRoles.FirstPersonControl.Thirdperson.Subcontrollers; + using PlayerRoles.FirstPersonControl.Thirdperson.Subcontrollers.Wearables; /// /// A wrapper class for . diff --git a/EXILED/Exiled.API/Features/Lockers/Chamber.cs b/EXILED/Exiled.API/Features/Lockers/Chamber.cs index 1b499294ae..e98281ed5c 100644 --- a/EXILED/Exiled.API/Features/Lockers/Chamber.cs +++ b/EXILED/Exiled.API/Features/Lockers/Chamber.cs @@ -16,6 +16,7 @@ namespace Exiled.API.Features.Lockers using Exiled.API.Extensions; using Exiled.API.Features.Pickups; using Exiled.API.Interfaces; + using InventorySystem.Items.Pickups; using MapGeneration.Distributors; using UnityEngine; @@ -77,13 +78,13 @@ public Chamber(LockerChamber chamber, Locker locker) /// public IEnumerable ToBeSpawned { - get => Base._toBeSpawned.Select(Pickup.Get); + get => Base.ToBeSpawned.Select(Pickup.Get); set { - Base._toBeSpawned.Clear(); + Base.ToBeSpawned.Clear(); foreach (Pickup pickup in value) - Base._toBeSpawned.Add(pickup.Base); + Base.ToBeSpawned.Add(pickup.Base); } } @@ -114,7 +115,7 @@ public IEnumerable AcceptableTypes public KeycardPermissions RequiredPermissions { get => (KeycardPermissions)Base.RequiredPermissions; - set => Base.RequiredPermissions = (Interactables.Interobjects.DoorUtils.KeycardPermissions)value; + set => Base.RequiredPermissions = (Interactables.Interobjects.DoorUtils.DoorPermissionFlags)value; } /// @@ -137,8 +138,8 @@ public bool UseMultipleSpawnpoints /// public Transform Spawnpoint { - get => Base._spawnpoint; - set => Base._spawnpoint = value; + get => Base.Spawnpoint; + set => Base.Spawnpoint = value; } /// @@ -146,8 +147,8 @@ public Transform Spawnpoint /// public bool InitiallySpawn { - get => Base._spawnOnFirstChamberOpening; - set => Base._spawnOnFirstChamberOpening = value; + get => Base.SpawnOnFirstChamberOpening; + set => Base.SpawnOnFirstChamberOpening = value; } /// @@ -155,8 +156,8 @@ public bool InitiallySpawn /// public float Cooldown { - get => Base._targetCooldown; - set => Base._targetCooldown = value; + get => Base.TargetCooldown; + set => Base.TargetCooldown = value; } /// @@ -188,6 +189,56 @@ public bool IsOpen /// public bool CanInteract => Base.CanInteract; + /// + /// Adds an item to the current chamber. + /// + /// The pickup to add. + public void AddItem(Pickup item) + { + Transform parent = UseMultipleSpawnpoints && Spawnpoints.Any() + ? Spawnpoints.GetRandomValue() + : Spawnpoint; + + if (IsOpen) + { + item.Transform.SetParent(parent); + + if (!item.IsSpawned) + item.Spawn(); + + return; + } + + // If the item is already spawned on the network, unspawn it before proceeding. + if (item.IsSpawned) + item.UnSpawn(); + + // Set the item's parent transform. + item.Transform.SetParent(parent); + + // Lock the item in place. + item.IsLocked = true; + + // Notify any pickup distributor triggers. + (item.Base as IPickupDistributorTrigger)?.OnDistributed(); + + // If the item has a Rigidbody component, make it kinematic and reset its position and rotation. + if (item.Rigidbody != null) + { + item.Rigidbody.isKinematic = true; + item.Rigidbody.transform.localPosition = Vector3.zero; + item.Rigidbody.transform.localRotation = Quaternion.identity; + + // Add the Rigidbody to the list of bodies to be unfrozen later. + SpawnablesDistributorBase.BodiesToUnfreeze.Add(item.Rigidbody); + } + + Base.Content.Add(item.Base); + item.Spawn(); + if (Base.WasEverOpened) + item.IsLocked = false; + } + /// /// Spawns a specified item from . /// @@ -219,7 +270,7 @@ public void AddItemToSpawn(ItemType itemType, int quantity = 1, bool spawnIfIsOp continue; } - Base._toBeSpawned.Add(pickup.Base); + Base.ToBeSpawned.Add(pickup.Base); } } @@ -245,6 +296,6 @@ public Vector3 GetRandomSpawnPoint() /// /// . /// . - internal static Chamber Get(LockerChamber chamber) => Chambers.TryGetValue(chamber, out Chamber chmb) ? chmb : new(chamber, Locker.Get(x => x.Chambers.Any(x => x.Base == chamber)).FirstOrDefault()); + internal static Chamber Get(LockerChamber chamber) => chamber == null ? null : Chambers.TryGetValue(chamber, out Chamber chmb) ? chmb : new(chamber, Locker.Get(x => x.Chambers.Any(x => x.Base == chamber)).FirstOrDefault()); } } diff --git a/EXILED/Exiled.API/Features/Lockers/Locker.cs b/EXILED/Exiled.API/Features/Lockers/Locker.cs index 653e432d2f..8c7f801d25 100644 --- a/EXILED/Exiled.API/Features/Lockers/Locker.cs +++ b/EXILED/Exiled.API/Features/Lockers/Locker.cs @@ -156,11 +156,11 @@ public Vector3 RandomChamberPosition /// The to filter by. If unspecified, all zones are considered. /// The to filter by. If unspecified, all locker types are considered. /// A random object, or if no matching locker is found. - public static Locker? Random(ZoneType zone = ZoneType.Unspecified, LockerType lockerType = LockerType.Unknow) + public static Locker? Random(ZoneType zone = ZoneType.Unspecified, LockerType lockerType = LockerType.Unknown) { IEnumerable filteredLockers = List; - if (lockerType != LockerType.Unknow) + if (lockerType != LockerType.Unknown) filteredLockers = filteredLockers.Where(l => l.Type == lockerType); if (zone != ZoneType.Unspecified) @@ -173,58 +173,7 @@ public Vector3 RandomChamberPosition /// Adds an item to a randomly selected locker chamber. /// /// The to be added to the locker chamber. - public void AddItem(Pickup item) - { - // Select a random chamber from the available locker chambers. - Chamber chamber = Chambers.GetRandomValue(); - - // Determine the parent transform where the item will be placed. - Transform parentTransform = chamber.UseMultipleSpawnpoints && chamber.Spawnpoints.Any() - ? chamber.Spawnpoints.GetRandomValue() - : chamber.Spawnpoint; - - // If the chamber is open, immediately set the item's parent and spawn it. - if (chamber.IsOpen) - { - item.Transform.SetParent(parentTransform); - - if(!item.IsSpawned) - item.Spawn(); - } - else - { - // If the item is already spawned on the network, unspawn it before proceeding. - if (item.IsSpawned) - item.UnSpawn(); - - // Set the item's parent transform. - item.Transform.SetParent(parentTransform); - - // Lock the item in place. - item.IsLocked = true; - - // Notify any pickup distributor triggers. - (item.Base as IPickupDistributorTrigger)?.OnDistributed(); - - // If the item has a Rigidbody component, make it kinematic and reset its position and rotation. - if (item.Rigidbody != null) - { - item.Rigidbody.isKinematic = true; - item.Rigidbody.transform.localPosition = Vector3.zero; - item.Rigidbody.transform.localRotation = Quaternion.identity; - - // Add the Rigidbody to the list of bodies to be unfrozen later. - SpawnablesDistributorBase.BodiesToUnfreeze.Add(item.Rigidbody); - } - - // If the chamber is configured to spawn items on the first opening, add the item to the list of items to be spawned. - // Otherwise, spawn the item immediately. - if (chamber.InitiallySpawn) - chamber.Base._toBeSpawned.Add(item.Base); - else - ItemDistributor.SpawnPickup(item.Base); - } - } + public void AddItem(Pickup item) => Chambers.GetRandomValue().AddItem(item); /// /// Spawns an item of the specified to the locker by creating a new . diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs index 4a953744f2..add2a5123b 100644 --- a/EXILED/Exiled.API/Features/Map.cs +++ b/EXILED/Exiled.API/Features/Map.cs @@ -8,6 +8,7 @@ namespace Exiled.API.Features { #pragma warning disable SA1401 + using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -27,6 +28,7 @@ namespace Exiled.API.Features using LightContainmentZoneDecontamination; using MapGeneration; using PlayerRoles.Ragdolls; + using RemoteAdmin; using UnityEngine; using Utils; using Utils.Networking; @@ -105,7 +107,25 @@ public static bool IsDecontaminationEnabled /// /// Gets the . /// - public static SqueakSpawner SqueakSpawner => squeakSpawner ??= Object.FindObjectOfType(); + public static SqueakSpawner SqueakSpawner => squeakSpawner ??= Object.FindFirstObjectByType(); + + /// + /// Sends a staff message to all players online with permission. + /// + /// The message to send. + /// The player to send message as, null will use Server Host. + public static void StaffMessage(string message, Player player = null) + { + player ??= Server.Host; + + foreach (Player target in Player.List) + { + if (!CommandProcessor.CheckPermissions(target.Sender, PlayerPermissions.AdminChat)) + continue; + + player.ReferenceHub.encryptedChannelManager.TrySendMessageToClient(player.NetId + "!" + message, EncryptedChannelManager.EncryptedChannel.AdminChat); + } + } /// /// Broadcasts a message to all players. @@ -133,6 +153,17 @@ public static void Broadcast(ushort duration, string message, global::Broadcast. Server.Broadcast.RpcAddElement(message, duration, type); } + /// + /// Broadcasts delegate invocation result to all players. + /// + /// The duration in seconds. + /// The delegate whose invocation result will be the message. + public static void Broadcast(ushort duration, Func func) + { + foreach (Player player in Player.List) + player.Broadcast(duration, func.Invoke(player)); + } + /// /// Shows a hint to all players. /// @@ -351,19 +382,9 @@ public static void ExplodeEffect(Vector3 position, ProjectileType projectileType /// The type of firearm to play the sound of. /// The maximum distance the sound can be heard from. /// The audio clip ID to play. + [Obsolete("This method is not working. Use PlayGunSound(Player, Vector3, FirearmType, float, int, bool) overload instead.")] public static void PlayGunSound(Vector3 position, ItemType firearmType, byte maxDistance = 45, byte audioClipId = 0) { - // TODO: Not finish - /* - GunAudioMessage msg = new() - { - Weapon = firearmType, - AudioClipId = audioClipId, - MaxDistance = maxDistance, - ShooterHub = ReferenceHub._hostHub, - ShooterPosition = new RelativePosition(position), - }; - msg.SendToAuthenticated();*/ } /// diff --git a/EXILED/Exiled.API/Features/Message.cs b/EXILED/Exiled.API/Features/Message.cs new file mode 100644 index 0000000000..1e82e0efbf --- /dev/null +++ b/EXILED/Exiled.API/Features/Message.cs @@ -0,0 +1,72 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features +{ + using System.ComponentModel; + + using Exiled.API.Enums; + + /// + /// A useful class for saving type-selective message configurations. + /// + public class Message + { + /// + /// Initializes a new instance of the class. + /// + public Message() + : this(string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The content of the message. + /// The duration of the message, in seconds. + /// Whether the message should be shown. + /// The type of the message. + public Message(string content, ushort duration = 10, bool show = true, MessageType type = MessageType.Broadcast) + { + Content = content; + Duration = duration; + Show = show; + Type = type; + } + + /// + /// Gets or sets the message content. + /// + [Description("The message content")] + public string Content { get; set; } + + /// + /// Gets or sets the message duration. + /// + [Description("The message duration")] + public ushort Duration { get; set; } + + /// + /// Gets or sets the message type. + /// + [Description("The message type")] + public MessageType Type { get; set; } + + /// + /// Gets or sets a value indicating whether the message should be shown. + /// + [Description("Indicates whether the message should be shown")] + public bool Show { get; set; } + + /// + /// Returns the Message in a human-readable format. + /// + /// A string containing Message-related data. + public override string ToString() => $"({Content}) {Duration} {Type}"; + } +} \ No newline at end of file diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs index e058efd079..7791218042 100644 --- a/EXILED/Exiled.API/Features/Npc.cs +++ b/EXILED/Exiled.API/Features/Npc.cs @@ -11,28 +11,30 @@ namespace Exiled.API.Features using System; using System.Collections.Generic; using System.Linq; - using System.Reflection; - using CentralAuth; using CommandSystem; using CommandSystem.Commands.RemoteAdmin.Dummies; using Exiled.API.Enums; - using Exiled.API.Features.Components; + using Exiled.API.Features.CustomStats; using Exiled.API.Features.Roles; using Footprinting; - using GameCore; using MEC; using Mirror; + using NetworkManagerUtils.Dummies; using PlayerRoles; + using PlayerStatsSystem; using UnityEngine; - using Object = UnityEngine.Object; - /// /// Wrapper class for handling NPC players. /// public class Npc : Player { + /// + /// The time it takes for the NPC to receive its and . + /// + public const float SpawnSetRoleDelay = 0.5f; + /// public Npc(ReferenceHub referenceHub) : base(referenceHub) @@ -252,10 +254,13 @@ public static Npc Spawn(string name, RoleTypeId role, Vector3 position) { Npc npc = new(DummyUtils.SpawnDummy(name)); - Timing.CallDelayed(0.5f, () => + Timing.CallDelayed(SpawnSetRoleDelay, () => { - npc.Role.Set(role); + npc.Role.Set(role, SpawnReason.ForceClass); npc.Position = position; + npc.CustomHealthStat = (HealthStat)npc.ReferenceHub.playerStats._dictionarizedTypes[typeof(HealthStat)]; + npc.Health = npc.MaxHealth; // otherwise the npc will spawn with 0 health + npc.ReferenceHub.playerStats._dictionarizedTypes[typeof(HumeShieldStat)] = npc.ReferenceHub.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HumeShieldStat))] = npc.CustomHumeShieldStat = new CustomHumeShieldStat { Hub = npc.ReferenceHub }; }); Dictionary.Add(npc.GameObject, npc); @@ -274,9 +279,12 @@ public static Npc Spawn(string name, RoleTypeId role = RoleTypeId.None, bool ign { Npc npc = new(DummyUtils.SpawnDummy(name)); - Timing.CallDelayed(0.5f, () => + Timing.CallDelayed(SpawnSetRoleDelay, () => { npc.Role.Set(role, SpawnReason.ForceClass, position is null ? RoleSpawnFlags.All : RoleSpawnFlags.AssignInventory); + npc.ReferenceHub.playerStats._dictionarizedTypes[typeof(HealthStat)] = npc.ReferenceHub.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HealthStat))] = npc.CustomHealthStat = new HealthStat { Hub = npc.ReferenceHub }; + npc.Health = npc.MaxHealth; // otherwise the npc will spawn with 0 health + npc.ReferenceHub.playerStats._dictionarizedTypes[typeof(HumeShieldStat)] = npc.ReferenceHub.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HumeShieldStat))] = npc.CustomHumeShieldStat = new CustomHumeShieldStat { Hub = npc.ReferenceHub }; if (position is not null) npc.Position = position.Value; diff --git a/EXILED/Exiled.API/Features/Paths.cs b/EXILED/Exiled.API/Features/Paths.cs index 153d8e0bf6..8d99b1a996 100644 --- a/EXILED/Exiled.API/Features/Paths.cs +++ b/EXILED/Exiled.API/Features/Paths.cs @@ -11,6 +11,8 @@ namespace Exiled.API.Features using System.IO; using System.Linq; + using LabApi.Loader; + /// /// A set of useful paths. /// @@ -106,7 +108,7 @@ public static void Reload(string rootDirectory = null) Dependencies = Path.Combine(Plugins, "dependencies"); Configs = Path.Combine(Exiled, "Configs"); IndividualConfigs = Path.Combine(Configs, "Plugins"); - LoaderConfig = PluginAPI.Loader.AssemblyLoader.InstalledPlugins.FirstOrDefault(x => x.PluginName == "Exiled Loader")?.MainConfigPath; + LoaderConfig = LabApi.Loader.PluginLoader.EnabledPlugins.FirstOrDefault(x => x.Name == "Exiled Loader")?.GetConfigPath("Exiled Loader"); Config = Path.Combine(Configs, $"{Server.Port}-config.yml"); BackupConfig = Path.Combine(Configs, $"{Server.Port}-config.yml.old"); IndividualTranslations = Path.Combine(Configs, "Translations"); diff --git a/EXILED/Exiled.API/Features/Pickups/ExplosiveGrenadePickup.cs b/EXILED/Exiled.API/Features/Pickups/ExplosiveGrenadePickup.cs index 061135573d..62484bcceb 100644 --- a/EXILED/Exiled.API/Features/Pickups/ExplosiveGrenadePickup.cs +++ b/EXILED/Exiled.API/Features/Pickups/ExplosiveGrenadePickup.cs @@ -96,8 +96,8 @@ protected override void InitializeProperties(ItemBase itemBase) base.InitializeProperties(itemBase); if (itemBase is ThrowableItem throwable && throwable.Projectile is ExplosionGrenade explosiveGrenade) { - MaxRadius = explosiveGrenade._maxRadius; - ScpDamageMultiplier = explosiveGrenade._scpDamageMultiplier; + MaxRadius = explosiveGrenade.MaxRadius; + ScpDamageMultiplier = explosiveGrenade.ScpDamageMultiplier; BurnDuration = explosiveGrenade._burnedDuration; DeafenDuration = explosiveGrenade._deafenedDuration; ConcussDuration = explosiveGrenade._concussedDuration; diff --git a/EXILED/Exiled.API/Features/Pickups/KeycardPickup.cs b/EXILED/Exiled.API/Features/Pickups/KeycardPickup.cs index 49115119b9..e7e29ff13e 100644 --- a/EXILED/Exiled.API/Features/Pickups/KeycardPickup.cs +++ b/EXILED/Exiled.API/Features/Pickups/KeycardPickup.cs @@ -42,9 +42,9 @@ internal KeycardPickup(ItemType type) } /// - /// Gets or sets the of the keycard. + /// Gets the of the keycard. /// - public KeycardPermissions Permissions { get; set; } + public KeycardPermissions Permissions { get; private set; } /// /// Gets the that this class is encapsulating. @@ -67,7 +67,18 @@ protected override void InitializeProperties(ItemBase itemBase) base.InitializeProperties(itemBase); if (itemBase is KeycardItem keycardItem) { - Permissions = (KeycardPermissions)keycardItem.Permissions; + foreach (DetailBase detail in keycardItem.Details) + { + switch (detail) + { + case PredefinedPermsDetail predefinedPermsDetail: + Permissions = (KeycardPermissions)predefinedPermsDetail.Levels.Permissions; + return; + case CustomPermsDetail customPermsDetail: + Permissions = (KeycardPermissions)customPermsDetail.GetPermissions(null); + return; + } + } } } } diff --git a/EXILED/Exiled.API/Features/Pickups/Projectiles/ExplosionGrenadeProjectile.cs b/EXILED/Exiled.API/Features/Pickups/Projectiles/ExplosionGrenadeProjectile.cs index 70a40ce880..07cdbebefb 100644 --- a/EXILED/Exiled.API/Features/Pickups/Projectiles/ExplosionGrenadeProjectile.cs +++ b/EXILED/Exiled.API/Features/Pickups/Projectiles/ExplosionGrenadeProjectile.cs @@ -49,8 +49,8 @@ internal ExplosionGrenadeProjectile(ItemType type) /// public float MaxRadius { - get => Base._maxRadius; - set => Base._maxRadius = value; + get => Base.MaxRadius; + set => Base.MaxRadius = value; } /// @@ -94,8 +94,8 @@ public float ConcussDuration /// public float ScpDamageMultiplier { - get => Base._scpDamageMultiplier; - set => Base._scpDamageMultiplier = value; + get => Base.ScpDamageMultiplier; + set => Base.ScpDamageMultiplier = value; } /// diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 6af35c8ca4..4dfcf09409 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -53,10 +53,8 @@ namespace Exiled.API.Features using PlayerRoles.Spectating; using PlayerRoles.Voice; using PlayerStatsSystem; - using PluginAPI.Core; using RelativePositioning; using RemoteAdmin; - using Respawning.NamingRules; using RoundRestarting; using UnityEngine; using Utils; @@ -96,8 +94,7 @@ public class Player : TypeCastObject, IEntity, IWorldSpace private readonly HashSet componentsInChildren = new(); private ReferenceHub referenceHub; - private CustomHealthStat healthStat; - private CustomHumeShieldStat humeShieldStat; + private Role role; /// @@ -145,6 +142,11 @@ public Player(GameObject gameObject) /// public static Dictionary UserIdsCache { get; } = new(20); + /// + /// Gets or sets a . + /// + public CustomHumeShieldStat CustomHumeShieldStat { get; protected set; } + /// public IReadOnlyCollection ComponentsInChildren => componentsInChildren; @@ -178,8 +180,8 @@ private set Inventory = value.inventory; CameraTransform = value.PlayerCameraReference; - value.playerStats._dictionarizedTypes[typeof(HealthStat)] = value.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HealthStat))] = healthStat = new CustomHealthStat { Hub = value }; - value.playerStats._dictionarizedTypes[typeof(HumeShieldStat)] = value.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HumeShieldStat))] = humeShieldStat = new CustomHumeShieldStat { Hub = value }; + CustomHealthStat = (HealthStat)value.playerStats._dictionarizedTypes[typeof(HealthStat)]; + value.playerStats._dictionarizedTypes[typeof(HumeShieldStat)] = value.playerStats.StatModules[Array.IndexOf(PlayerStats.DefinedModules, typeof(HumeShieldStat))] = CustomHumeShieldStat = new CustomHumeShieldStat { Hub = value }; } } @@ -349,37 +351,9 @@ public string CustomInfo get => ReferenceHub.nicknameSync.Network_customPlayerInfoString; set { - // NW Client check. - if (value.Contains('<')) + if (!NicknameSync.ValidateCustomInfo(value, out string rejectionText)) { - foreach (string token in value.Split('<')) - { - if (token.StartsWith("/", StringComparison.Ordinal) || - token.StartsWith("b>", StringComparison.Ordinal) || - token.StartsWith("i>", StringComparison.Ordinal) || - token.StartsWith("size=", StringComparison.Ordinal) || - token.Length is 0) - continue; - - if (token.StartsWith("color=", StringComparison.Ordinal)) - { - if (token.Length < 14 || token[13] != '>') - Log.Error($"Custom info of player {Nickname} has been REJECTED. \nreason: (Bad text reject) \ntoken: {token} \nInfo: {value}"); - else if (!Misc.AllowedColors.ContainsValue(token.Substring(6, 7))) - Log.Error($"Custom info of player {Nickname} has been REJECTED. \nreason: (Bad color reject) \ntoken: {token} \nInfo: {value}"); - } - else if (token.StartsWith("#", StringComparison.Ordinal)) - { - if (token.Length < 8 || token[7] != '>') - Log.Error($"Custom info of player {Nickname} has been REJECTED. \nreason: (Bad text reject) \ntoken: {token} \nInfo: {value}"); - else if (!Misc.AllowedColors.ContainsValue(token.Substring(0, 7))) - Log.Error($"Custom info of player {Nickname} has been REJECTED. \nreason: (Bad color reject) \ntoken: {token} \nInfo: {value}"); - } - else - { - Log.Error($"Custom info of player {Nickname} has been REJECTED. \nreason: (Bad color reject) \ntoken: {token} \nInfo: {value}"); - } - } + Log.Error($"Could not set CustomInfo for {Nickname}. Reason: {rejectionText}"); } InfoArea = string.IsNullOrEmpty(value) ? InfoArea & ~PlayerInfoArea.CustomInfo : InfoArea |= PlayerInfoArea.CustomInfo; @@ -422,7 +396,7 @@ public float InfoViewRange /// /// /// - public bool HasReservedSlot => ReservedSlot.HasReservedSlot(UserId, out _); + public bool HasReservedSlot => ReservedSlot.HasReservedSlot(UserId); /// /// Gets a value indicating whether the player is in whitelist. @@ -509,7 +483,7 @@ public Player Cuffer public virtual Vector3 Position { get => Transform.position; - set => ReferenceHub.TryOverridePosition(value, Vector3.zero); + set => ReferenceHub.TryOverridePosition(value); } /// @@ -529,7 +503,7 @@ public RelativePosition RelativePosition public Quaternion Rotation { get => Transform.rotation; - set => ReferenceHub.TryOverridePosition(Position, value.eulerAngles); + set => ReferenceHub.TryOverrideRotation(value.eulerAngles); } /// @@ -867,13 +841,13 @@ public DangerStackBase[] Dangers /// public float Health { - get => healthStat.CurValue; + get => CustomHealthStat.CurValue; set { if (value > MaxHealth) MaxHealth = value; - healthStat.CurValue = value; + CustomHealthStat.CurValue = value; } } @@ -882,8 +856,8 @@ public float Health /// public float MaxHealth { - get => healthStat.MaxValue; - set => healthStat.CustomMaxValue = value; + get => CustomHealthStat.MaxValue; + set => CustomHealthStat.MaxValue = value; } /// @@ -929,8 +903,8 @@ public float MaxArtificialHealth /// This value can bypass the role's hume shield maximum. However, this value will only be visible to the end-player as Hume Shield if is . Otherwise, the game will treat the player as though they have the amount of Hume Shield specified, even though they cannot see it. public float HumeShield { - get => HumeShieldStat.CurValue; - set => HumeShieldStat.CurValue = value; + get => CustomHumeShieldStat.CurValue; + set => CustomHumeShieldStat.CurValue = value; } /// @@ -938,8 +912,8 @@ public float HumeShield /// public float MaxHumeShield { - get => humeShieldStat.MaxValue; - set => humeShieldStat.CustomMaxValue = value; + get => CustomHumeShieldStat.MaxValue; + set => CustomHumeShieldStat.MaxValue = value; } /// @@ -947,8 +921,8 @@ public float MaxHumeShield /// public float HumeShieldRegenerationMultiplier { - get => humeShieldStat.ShieldRegenerationMultiplier; - set => humeShieldStat.ShieldRegenerationMultiplier = value; + get => CustomHumeShieldStat.ShieldRegenerationMultiplier; + set => CustomHumeShieldStat.ShieldRegenerationMultiplier = value; } /// @@ -958,9 +932,9 @@ public float HumeShieldRegenerationMultiplier /// /// Gets the player's . - /// TODO: Change to . /// - public HumeShieldStat HumeShieldStat => humeShieldStat; + [Obsolete("Use " + nameof(CustomHumeShieldStat) + " instead.")] + public HumeShieldStat HumeShieldStat => CustomHumeShieldStat; /// /// Gets or sets the item in the player's hand. Value will be if the player is not holding anything. @@ -971,6 +945,9 @@ public Item CurrentItem get => Item.Get(Inventory.CurInstance); set { + if (CurrentItem is MicroHid microHid) + microHid.State = InventorySystem.Items.MicroHID.Modules.MicroHidPhase.Standby; + if (value is null || value.Type == ItemType.None) { Inventory.ServerSelectItem(0); @@ -1014,8 +991,8 @@ public float Stamina /// public string GroupName { - get => ServerStatic.PermissionsHandler._members.TryGetValue(UserId, out string groupName) ? groupName : null; - set => ServerStatic.PermissionsHandler._members[UserId] = value; + get => ServerStatic.PermissionsHandler.Members.TryGetValue(UserId, out string groupName) ? groupName : null; + set => ServerStatic.PermissionsHandler.Members[UserId] = value; } /// @@ -1182,11 +1159,23 @@ public bool IsSpawnProtected internal static ConditionalWeakTable UnverifiedPlayers { get; } = new(); /// - /// Converts NwPluginAPI player to EXILED player. + /// Gets or sets a . + /// + protected HealthStat CustomHealthStat { get; set; } + + /// + /// Converts LabApi player to EXILED player. + /// + /// The LabApi player. + /// EXILED player. + public static implicit operator Player(LabApi.Features.Wrappers.Player player) => Get(player); + + /// + /// Converts LabApi player to EXILED player. /// - /// The NwPluginAPI player. + /// The LabApi player. /// EXILED player. - public static implicit operator Player(PluginAPI.Core.Player player) => Get(player); + public static implicit operator LabApi.Features.Wrappers.Player(Player player) => LabApi.Features.Wrappers.Player.Get(player.ReferenceHub); /// /// Gets a filtered by side. Can be empty. @@ -1377,11 +1366,11 @@ public static Player Get(string args) } /// - /// Gets the from NwPluginAPI class. + /// Gets the from LabApi class. /// - /// The class. + /// The class. /// A or if not found. - public static Player Get(PluginAPI.Core.Player apiPlayer) => Get(apiPlayer.ReferenceHub); + public static Player Get(LabApi.Features.Wrappers.Player apiPlayer) => Get(apiPlayer.ReferenceHub); /// /// Try-get a player given a . @@ -1464,12 +1453,12 @@ public static Player Get(string args) public static bool TryGet(string args, out Player player) => (player = Get(args)) is not null; /// - /// Try-get the from NwPluginAPI class. + /// Try-get the from LabApi class. /// - /// The class. + /// The class. /// The player found or if not found. /// A boolean indicating whether a player was found. - public static bool TryGet(PluginAPI.Core.Player apiPlayer, out Player player) => (player = Get(apiPlayer)) is not null; + public static bool TryGet(LabApi.Features.Wrappers.Player apiPlayer, out Player player) => (player = Get(apiPlayer)) is not null; /// /// Try-get player by . @@ -1508,10 +1497,10 @@ public static bool AddReservedSlot(string userId, bool isPermanent) { if (isPermanent) { - if (ReservedSlots.HasReservedSlot(userId)) + if (LabApi.Features.Wrappers.ReservedSlots.HasReservedSlot(userId)) return false; - ReservedSlots.Add(userId); + LabApi.Features.Wrappers.ReservedSlots.Add(userId); return true; } @@ -1532,7 +1521,7 @@ public static bool AddToWhitelist(string userId, bool isPermanent) if (WhiteList.IsOnWhitelist(userId)) return false; - Whitelist.Add(userId); + LabApi.Features.Wrappers.Whitelist.Add(userId); return true; } @@ -1824,7 +1813,7 @@ public bool TryGetItem(ushort serial, out Item item) /// The group to be set. public void SetRank(string name, UserGroup group) { - if (ServerStatic.GetPermissionsHandler()._groups.TryGetValue(name, out UserGroup userGroup)) + if (ServerStatic.PermissionsHandler.Groups.TryGetValue(name, out UserGroup userGroup)) { userGroup.BadgeColor = group.BadgeColor; userGroup.BadgeText = name; @@ -1835,15 +1824,15 @@ public void SetRank(string name, UserGroup group) } else { - ServerStatic.GetPermissionsHandler()._groups.Add(name, group); + ServerStatic.PermissionsHandler.Groups.Add(name, group); ReferenceHub.serverRoles.SetGroup(group, false, false); } - if (ServerStatic.GetPermissionsHandler()._members.ContainsKey(UserId)) - ServerStatic.GetPermissionsHandler()._members[UserId] = name; + if (ServerStatic.PermissionsHandler.Members.ContainsKey(UserId)) + ServerStatic.PermissionsHandler.Members[UserId] = name; else - ServerStatic.GetPermissionsHandler()._members.Add(UserId, name); + ServerStatic.PermissionsHandler.Members.Add(UserId, name); } /// @@ -2907,6 +2896,26 @@ public bool TryAddCandy(CandyKindID candyType) return true; } + /// + /// Removes specific candy from the players . + /// + /// The to remove. + /// Remove all candy of that type. + /// if a candy was removed. + public bool TryRemoveCandу(CandyKindID candyType, bool removeAll = false) + { + foreach (Item item in Items) + { + if (item is not Scp330 bag) + continue; + + if (bag.RemoveCandy(candyType, removeAll) > 0) + return true; + } + + return false; + } + /// /// Resets the player's inventory to the provided list of items, clearing any items it already possess. /// @@ -2953,9 +2962,6 @@ public void ClearInventory(bool destroy = true) /// public void ClearItems(bool destroy = true) { - if (CurrentArmor is Armor armor) - armor.RemoveExcessOnDrop = false; - while (Items.Count > 0) RemoveItem(Items.ElementAt(0), destroy); } @@ -3029,6 +3035,36 @@ public void ShowHint(Hint hint) ShowHint(hint.Content, hint.Duration); } + /// + /// Messages the given to the player. + /// + /// The to be messaged. + /// Clears all player's messages before sending the new one. + public void Message(Message message, bool shouldClearPrevious = false) + { + if (message.Show) + Message(message.Duration, message.Content, message.Type, shouldClearPrevious); + } + + /// + /// Shows a message to the player. + /// + /// The message duration. + /// The message to be messaged. + /// The message type. + /// Clears all player's messages before sending the new one. + public void Message(ushort duration, string message, MessageType type = MessageType.Broadcast, bool shouldClearPrevious = false) + { + if (type == MessageType.Broadcast) + { + Broadcast(duration, message, shouldClearPrevious: shouldClearPrevious); + } + else + { + ShowHint(message, duration); + } + } + /// /// Sends a HitMarker to the player. /// @@ -3444,6 +3480,21 @@ public void AddAhp(float amount, float limit = 75f, float decay = 1.2f, float ef .ServerAddProcess(amount, limit, decay, efficacy, sustain, persistant); } + /// + /// Adds a new to the player. + /// + /// The delay before regeneration starts (in seconds). + /// Health points regenerated per second. + /// Total duration of the regeneration (in seconds). + /// How fast the regeneration progresses (default is 1.0). + /// Multiplier for HP amount being regenerated (default is 1.0). + public void AddRegeneration(float starttime = 0f, float rate = 1f, float duration = 1f, float speedMultiplier = 1f, float healthPointsMultiplier = 1f) + { + AnimationCurve regenCurve = AnimationCurve.Constant(starttime, duration, rate); + UsableItemsController.GetHandler(ReferenceHub) + .ActiveRegenerations.Add(new RegenerationProcess(regenCurve, speedMultiplier, healthPointsMultiplier)); + } + /// /// Reconnects the player to the server. Can be used to redirect them to another server on a different port but same IP. /// @@ -3460,8 +3511,13 @@ public void Reconnect(ushort newPort = 0, float delay = 5, bool reconnect = true } /// - public void PlayGunSound(ItemType type, byte volume, byte audioClipId = 0) => - MirrorExtensions.PlayGunSound(this, Position, type, volume, audioClipId); + [Obsolete("Use PlayGunSound(Player, Vector3, FirearmType, byte, byte) instead.")] + public void PlayGunSound(ItemType type, byte volume, byte audioClipId = 0) + => PlayGunSound(type.GetFirearmType(), volume, audioClipId); + + /// + public void PlayGunSound(FirearmType itemType, float pitch = 1, int clipIndex = 0) => + this.PlayGunSound(Position, itemType, pitch, clipIndex); /// public void PlaceBlood(Vector3 direction) => Map.PlaceBlood(Position, direction); @@ -3529,7 +3585,7 @@ public void Teleport(object obj, Vector3 offset) Teleport(locker.transform.position + Vector3.up + offset); break; case LockerChamber chamber: - Teleport(chamber._spawnpoint.position + Vector3.up + offset); + Teleport(chamber.Spawnpoint.position + Vector3.up + offset); break; case ElevatorChamber elevator: Teleport(elevator.transform.position + Vector3.up + offset); diff --git a/EXILED/Exiled.API/Features/Ragdoll.cs b/EXILED/Exiled.API/Features/Ragdoll.cs index 8cde5d8cb2..011c092248 100644 --- a/EXILED/Exiled.API/Features/Ragdoll.cs +++ b/EXILED/Exiled.API/Features/Ragdoll.cs @@ -101,7 +101,7 @@ public RagdollData NetworkInfo public DamageHandlerBase DamageHandler { get => NetworkInfo.Handler; - set => NetworkInfo = new(NetworkInfo.OwnerHub, value, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Nickname, NetworkInfo.CreationTime); + set => NetworkInfo = new(NetworkInfo.OwnerHub, value, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Scale, NetworkInfo.Nickname, NetworkInfo.CreationTime); } /// @@ -145,7 +145,16 @@ public bool CanBeCleanedUp public string Nickname { get => NetworkInfo.Nickname; - set => NetworkInfo = new(NetworkInfo.OwnerHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, value, NetworkInfo.CreationTime); + set => NetworkInfo = new(NetworkInfo.OwnerHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Scale, value, NetworkInfo.CreationTime); + } + + /// + /// Gets or sets the ragdoll's Scale with RagdollData. + /// + public Vector3 Scale + { + get => NetworkInfo.Scale; + set => NetworkInfo = new(NetworkInfo.OwnerHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, value, NetworkInfo.Nickname, NetworkInfo.CreationTime); } /// @@ -159,7 +168,7 @@ public string Nickname public Player Owner { get => Player.Get(NetworkInfo.OwnerHub); - set => NetworkInfo = new(value.ReferenceHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Nickname, NetworkInfo.CreationTime); + set => NetworkInfo = new(value.ReferenceHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Scale, NetworkInfo.Nickname, NetworkInfo.CreationTime); } /// @@ -171,7 +180,7 @@ public DateTime CreationTime set { float creationTime = (float)(NetworkTime.time - (DateTime.Now - value).TotalSeconds); - NetworkInfo = new RagdollData(NetworkInfo.OwnerHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Nickname, creationTime); + NetworkInfo = new RagdollData(NetworkInfo.OwnerHub, NetworkInfo.Handler, NetworkInfo.RoleType, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Scale, NetworkInfo.Nickname, creationTime); } } @@ -181,7 +190,7 @@ public DateTime CreationTime public RoleTypeId Role { get => NetworkInfo.RoleType; - set => NetworkInfo = new(NetworkInfo.OwnerHub, NetworkInfo.Handler, value, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Nickname, NetworkInfo.CreationTime); + set => NetworkInfo = new(NetworkInfo.OwnerHub, NetworkInfo.Handler, value, NetworkInfo.StartPosition, NetworkInfo.StartRotation, NetworkInfo.Scale, NetworkInfo.Nickname, NetworkInfo.CreationTime); } /// @@ -248,9 +257,9 @@ public Quaternion Rotation } /// - /// Gets or sets the ragdoll's scale. + /// Gets or sets the ragdoll's as Gameobjectscale. /// - public Vector3 Scale + public Vector3 RagdollScale { get => Base.transform.localScale; set diff --git a/EXILED/Exiled.API/Features/Roles/FpcRole.cs b/EXILED/Exiled.API/Features/Roles/FpcRole.cs index ecc946c9b7..d1c0a12591 100644 --- a/EXILED/Exiled.API/Features/Roles/FpcRole.cs +++ b/EXILED/Exiled.API/Features/Roles/FpcRole.cs @@ -27,7 +27,6 @@ namespace Exiled.API.Features.Roles /// public abstract class FpcRole : Role, IVoiceRole { - private static FieldInfo enableFallDamageField; private bool isUsingStamina = true; /// @@ -68,17 +67,31 @@ public RelativePosition ClientRelativePosition set => FirstPersonController.FpcModule.Motor.ReceivedPosition = value; } + /// + /// Gets or sets the player's gravity. + /// + public Vector3 Gravity + { + get => FirstPersonController.FpcModule.Motor.GravityController.Gravity; + set => FirstPersonController.FpcModule.Motor.GravityController.Gravity = value; + } + /// /// Gets or sets a value indicating whether if the player should get damage. /// public bool IsFallDamageEnable { - get => FirstPersonController.FpcModule.Motor._enableFallDamage; - set - { - enableFallDamageField ??= AccessTools.Field(typeof(FpcMotor), nameof(FpcMotor._enableFallDamage)); - enableFallDamageField.SetValue(FirstPersonController.FpcModule.Motor, value); - } + get => FirstPersonController.FpcModule.Motor._fallDamageSettings.Enabled; + set => FirstPersonController.FpcModule.Motor._fallDamageSettings.Enabled = value; + } + + /// + /// Gets or sets the multiplier of damage. + /// + public float FallDamageMultiplier + { + get => FirstPersonController.FpcModule.Motor._fallDamageSettings.Multiplier; + set => FirstPersonController.FpcModule.Motor._fallDamageSettings.Multiplier = value; } /// diff --git a/EXILED/Exiled.API/Features/Roles/HumanRole.cs b/EXILED/Exiled.API/Features/Roles/HumanRole.cs index 8a238a3e7d..767a010d45 100644 --- a/EXILED/Exiled.API/Features/Roles/HumanRole.cs +++ b/EXILED/Exiled.API/Features/Roles/HumanRole.cs @@ -8,7 +8,7 @@ namespace Exiled.API.Features.Roles { using PlayerRoles; - + using PlayerRoles.PlayableScps.HumeShield; using Respawning; using Respawning.NamingRules; @@ -17,7 +17,7 @@ namespace Exiled.API.Features.Roles /// /// Defines a role that represents a human class. /// - public class HumanRole : FpcRole + public class HumanRole : FpcRole, IHumeShieldRole { /// /// Initializes a new instance of the class. @@ -27,6 +27,7 @@ internal HumanRole(HumanGameRole baseRole) : base(baseRole) { Base = baseRole; + HumeShieldModule = baseRole.HumeShieldModule; } /// @@ -56,6 +57,9 @@ public byte UnitNameId /// public new HumanGameRole Base { get; } + /// + public HumeShieldModuleBase HumeShieldModule { get; } + /// /// Gets the armor efficacy based on a specific and the armor the is wearing. /// diff --git a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs index 9c0ac35eb4..6a58fcd444 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs @@ -118,40 +118,6 @@ internal Scp049Role(Scp049GameRole baseRole) /// public IEnumerable DeadZombies => Scp049ResurrectAbility.DeadZombies.Select(x => Player.Get(x)); - // TODO: ReAdd Setter but before making an propper way to overwrite NW constant only when the propperty has been used -#pragma warning disable SA1623 // Property summary documentation should match accessors -#pragma warning disable SA1202 - /// - /// Gets or sets how mush time the Call Ability will be effective. - /// - internal double CallAbilityDuration { get; } = Scp049CallAbility.EffectDuration; - - /// - /// Gets or sets the Cooldown of the Call Ability. - /// - internal double CallAbilityBaseCooldown { get; } = Scp049CallAbility.BaseCooldown; - - /// - /// Gets or sets the Cooldown of the Sense Ability. - /// - internal double SenseAbilityBaseCooldown { get; } = Scp049SenseAbility.BaseCooldown; - - /// - /// Gets or sets the Cooldown of the Sense Ability when you lost your target. - /// - internal double SenseAbilityReducedCooldown { get; } = Scp049SenseAbility.ReducedCooldown; - - /// - /// Gets or sets the Cooldown of the Sense Ability when it's failed. - /// - internal double SenseAbilityDuration { get; } = Scp049SenseAbility.EffectDuration; - - /// - /// Gets or sets how mush time the Sense Ability will be effective. - /// - internal double SenseAbilityFailCooldown { get; } = Scp049SenseAbility.AttemptFailCooldown; -#pragma warning restore SA1623 // Property summary documentation should match accessors - /// /// Gets all the resurrected players. /// @@ -319,7 +285,7 @@ public void Sense(Player player) if (SenseAbility.Target is null) { - SenseAbility.Cooldown.Trigger(SenseAbilityFailCooldown); + SenseAbility.Cooldown.Trigger(Scp049SenseAbility.AttemptFailCooldown); SenseAbility.ServerSendRpc(true); return; } @@ -332,7 +298,7 @@ public void Sense(Player player) if (!VisionInformation.GetVisionInformation(SenseAbility.Owner, SenseAbility.Owner.PlayerCameraReference, humanRole.CameraPosition, radius, SenseAbility._distanceThreshold).IsLooking) return; - SenseAbility.Duration.Trigger(SenseAbilityDuration); + SenseAbility.Duration.Trigger(Scp049SenseAbility.EffectDuration); SenseAbility.HasTarget = true; SenseAbility.ServerSendRpc(true); } diff --git a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs index 2480ca3d88..c2b8cb70ce 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs @@ -593,7 +593,7 @@ public void ActivateTesla(bool consumeEnergy = true) Scp079Camera cam = CurrentCameraSync.CurrentCamera; RewardManager.MarkRoom(cam.Room); - if (!global::TeslaGate.AllGates.TryGetFirst(x => RoomIdUtils.IsTheSameRoom(cam.Position, x.transform.position), out global::TeslaGate teslaGate)) + if (!global::TeslaGate.AllGates.TryGetFirst(x => cam.Position.TryGetRoom(out RoomIdentifier camRoom) && x.transform.position.TryGetRoom(out RoomIdentifier teslaRoom) && camRoom == teslaRoom, out global::TeslaGate teslaGate)) return; if (consumeEnergy) diff --git a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs index 65ab01864a..4396f8f87b 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs @@ -90,11 +90,6 @@ internal Scp939Role(Scp939GameRole baseRole) /// ~Scp939Role() => ListPool.Pool.Return(VisiblePlayers); - /// - /// Gets a list of players who are turned away from SCP-939 AmnesticCloud Ability. - /// - public static HashSet TurnedPlayers { get; } = new(20); - /// public override RoleTypeId Type { get; } = RoleTypeId.Scp939; diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs index 9fde18957c..e5bcc9c0d5 100644 --- a/EXILED/Exiled.API/Features/Room.cs +++ b/EXILED/Exiled.API/Features/Room.cs @@ -246,7 +246,7 @@ public static Room Get(RoomIdentifier roomIdentifier) => roomIdentifier == null /// /// The to search for. /// The with the given or if not found. - public static Room Get(Vector3 position) => RoomIdUtils.RoomAtPositionRaycasts(position, false) is RoomIdentifier identifier ? Get(identifier) : null; + public static Room Get(Vector3 position) => position.TryGetRoom(out RoomIdentifier room) ? Get(room) : null; /// /// Gets a given the specified . @@ -421,7 +421,7 @@ internal void InternalCreate() Identifier = gameObject.GetComponent(); RoomIdentifierToRoom.Add(Identifier, this); - Zone = FindZone(gameObject); + Zone = Identifier.Zone.GetZone(); #if DEBUG if (Zone is ZoneType.Unspecified) Log.Error($"[ZONETYPE UNKNOWN] {this} Zone : {Identifier?.Zone}"); @@ -496,6 +496,8 @@ private static RoomType FindType(GameObject gameObject) "HCZ_Straight Variant" => RoomType.HczStraightVariant, "HCZ_ChkpA" => RoomType.HczElevatorA, "HCZ_ChkpB" => RoomType.HczElevatorB, + "HCZ_127" => RoomType.Hcz127, + "HCZ_ServerRoom" => RoomType.HczServerRoom, "EZ_GateA" => RoomType.EzGateA, "EZ_GateB" => RoomType.EzGateB, "EZ_ThreeWay" => RoomType.EzTCross, @@ -527,22 +529,5 @@ private static RoomType FindType(GameObject gameObject) _ => RoomType.Unknown, }; } - - private static ZoneType FindZone(GameObject gameObject) - { - Transform transform = gameObject.transform; - - if (gameObject.name == "PocketWorld") - return ZoneType.Pocket; - - 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, - }; - } } } diff --git a/EXILED/Exiled.API/Features/Round.cs b/EXILED/Exiled.API/Features/Round.cs index b6ee2b35fc..a278e169dc 100644 --- a/EXILED/Exiled.API/Features/Round.cs +++ b/EXILED/Exiled.API/Features/Round.cs @@ -54,7 +54,7 @@ public static class Round /// /// Gets a value indicating whether the round is ended. /// - public static bool IsEnded => RoundSummary._singletonSet && RoundSummary.singleton._roundEnded; + public static bool IsEnded => RoundSummary._singletonSet && RoundSummary.singleton.IsRoundEnded; /// /// Gets a value indicating whether the round is lobby. diff --git a/EXILED/Exiled.API/Features/Scp914.cs b/EXILED/Exiled.API/Features/Scp914.cs index 57ee9d366c..7d9ea65818 100644 --- a/EXILED/Exiled.API/Features/Scp914.cs +++ b/EXILED/Exiled.API/Features/Scp914.cs @@ -40,8 +40,8 @@ public static Scp914KnobSetting KnobStatus /// public static Scp914Mode ConfigMode { - get => Scp914Controller._configMode.Value; - set => Scp914Controller._configMode.Value = value; + get => Scp914Controller.ConfigMode.Value; + set => Scp914Controller.ConfigMode.Value = value; } /// @@ -72,7 +72,7 @@ public static Scp914Mode ConfigMode /// /// Gets a value indicating whether SCP-914 is active and currently processing items. /// - public static bool IsWorking => Scp914Controller._isUpgrading; + public static bool IsWorking => Scp914Controller.IsUpgrading; /// /// Gets a value indicating all of the GameObjects currently present inside SCP-914's intake chamber. @@ -92,7 +92,7 @@ public static Scp914Mode ConfigMode /// /// Gets the list with which SCP-914 has. /// - public static IReadOnlyCollection Doors => Scp914Controller._doors.Select(Door.Get).ToList(); + public static IReadOnlyCollection Doors => Scp914Controller.Doors.Select(Door.Get).ToList(); /// /// Filters all GameObjects inside SCP-914's intake chamber into players and items. diff --git a/EXILED/Exiled.API/Features/Server.cs b/EXILED/Exiled.API/Features/Server.cs index 57d1752725..5de5dd5655 100644 --- a/EXILED/Exiled.API/Features/Server.cs +++ b/EXILED/Exiled.API/Features/Server.cs @@ -60,11 +60,11 @@ public static class Server /// public static string Name { - get => ServerConsole._serverName; + get => ServerConsole.ServerName; set { - ServerConsole._serverName = value; - ServerConsole.singleton.RefreshServerName(); + ServerConsole.ServerName = value; + ServerConsole.Singleton.RefreshServerNameSafe(); } } diff --git a/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs b/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs index ad0019ce48..405a285098 100644 --- a/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs +++ b/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs @@ -4,12 +4,13 @@ // Licensed under the CC BY-SA 3.0 license. // // ----------------------------------------------------------------------- + namespace Exiled.API.Features.Spawn { using System; - using System.Linq; using Exiled.API.Enums; + using Exiled.API.Extensions; using Exiled.API.Features.Lockers; using UnityEngine; using YamlDotNet.Serialization; @@ -38,7 +39,7 @@ public class LockerSpawnPoint : SpawnPoint /// /// Gets or sets the type of the . /// - public LockerType Type { get; set; } = LockerType.Unknow; + public LockerType Type { get; set; } = LockerType.Unknown; /// public override float Chance { get; set; } @@ -57,16 +58,24 @@ public override Vector3 Position { get { - Locker foundLocker = Locker.Random(Zone, Type) ?? throw new NullReferenceException("No locker found in the specified zone."); - - // If UseChamber is true, use a random chamber's position. - if (UseChamber) - return foundLocker.RandomChamberPosition; - - // Otherwise, use the Offset if provided, or the locker's position. - return Offset != Vector3.zero ? foundLocker.Transform.TransformPoint(Offset) : foundLocker.Position; + GetSpawningInfo(out _, out _, out Vector3 position); + return position; } set => throw new InvalidOperationException("The position of this type of SpawnPoint cannot be changed."); } + + /// + /// Gets the spawn info. + /// + /// The locker to spawn in. + /// The chamber to spawn in. Null when is false. + /// The position to spawn in. + /// No locker was found. + public void GetSpawningInfo(out Locker locker, out Chamber chamber, out Vector3 position) + { + locker = Locker.Random(Zone, Type) ?? throw new NullReferenceException($"No locker found of type {Type} in {Zone}."); + chamber = UseChamber ? locker.Chambers.GetRandomValue() : null; + position = chamber?.GetRandomSpawnPoint() ?? (Offset == Vector3.zero ? locker.Position : locker.Transform.TransformPoint(Offset)); + } } } diff --git a/EXILED/Exiled.API/Features/Toys/AdminToy.cs b/EXILED/Exiled.API/Features/Toys/AdminToy.cs index a3ffa539cd..4a82030c8c 100644 --- a/EXILED/Exiled.API/Features/Toys/AdminToy.cs +++ b/EXILED/Exiled.API/Features/Toys/AdminToy.cs @@ -166,6 +166,10 @@ public static AdminToy Get(AdminToyBase adminToyBase) PrimitiveObjectToy primitiveObjectToy => new Primitive(primitiveObjectToy), ShootingTarget shootingTarget => new ShootingTargetToy(shootingTarget), SpeakerToy speakerToy => new Speaker(speakerToy), + CapybaraToy capybaraToy => new Capybara(capybaraToy), + Scp079CameraToy scp079CameraToy => new CameraToy(scp079CameraToy), + InvisibleInteractableToy invisibleInteractableToy => new InteractableToy(invisibleInteractableToy), + TextToy textToy => new Text(textToy), _ => throw new System.NotImplementedException() }; } diff --git a/EXILED/Exiled.API/Features/Toys/CameraToy.cs b/EXILED/Exiled.API/Features/Toys/CameraToy.cs new file mode 100644 index 0000000000..518c4d789a --- /dev/null +++ b/EXILED/Exiled.API/Features/Toys/CameraToy.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Toys +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using AdminToys; + using Exiled.API.Enums; + using Exiled.API.Interfaces; + using UnityEngine; + + /// + /// A wrapper class for . + /// + internal class CameraToy : AdminToy, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// The of the toy. + internal CameraToy(Scp079CameraToy scp079CameraToy) + : base(scp079CameraToy, AdminToyType.CameraToy) => Base = scp079CameraToy; + + /// + /// Gets the base . + /// + public Scp079CameraToy Base { get; } + + /// + /// Gets or sets the Vertical Restriction. + /// + public Vector2 VerticalConstraint + { + get => Base.NetworkVerticalConstraint; + set => Base.NetworkVerticalConstraint = value; + } + + /// + /// Gets or sets the Horizontal restriction. + /// + public Vector2 HorizontalConstraint + { + get => Base.NetworkHorizontalConstraint; + set => Base.NetworkHorizontalConstraint = value; + } + + /// + /// Gets or sets the Zoom restriction. + /// + public Vector2 ZoomConstraint + { + get => Base.NetworkZoomConstraint; + set => Base.NetworkZoomConstraint = value; + } + + /// + /// Gets or sets the Room where the Camera is associated with. + /// + public Room Room + { + get => Room.Get(Base.NetworkRoom); + set => Base.NetworkRoom = value.Identifier; + } + + /// + /// Gets or sets the Name of the Camera. + /// + public string Name + { + get => Base.NetworkLabel; + set => Base.NetworkLabel = value; + } + } +} diff --git a/EXILED/Exiled.API/Features/Toys/Capybara.cs b/EXILED/Exiled.API/Features/Toys/Capybara.cs new file mode 100644 index 0000000000..b88dbfd9c8 --- /dev/null +++ b/EXILED/Exiled.API/Features/Toys/Capybara.cs @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Toys +{ + using AdminToys; + using Enums; + using Exiled.API.Interfaces; + + /// + /// A wrapper class for . + /// + public class Capybara : AdminToy, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// The of the toy. + internal Capybara(CapybaraToy speakerToy) + : base(speakerToy, AdminToyType.Speaker) => Base = speakerToy; + + /// + /// Gets the prefab. + /// + public static CapybaraToy Prefab => PrefabHelper.GetPrefab(PrefabType.CapybaraToy); + + /// + /// Gets the base . + /// + public CapybaraToy Base { get; } + + /// + /// Gets or sets a value indicating whether the capybara can be collided with. + /// + public bool Collidable + { + get => Base.Network_collisionsEnabled; + set => Base.Network_collisionsEnabled = value; + } + } +} diff --git a/EXILED/Exiled.API/Features/Toys/InteractableToy.cs b/EXILED/Exiled.API/Features/Toys/InteractableToy.cs new file mode 100644 index 0000000000..c6c73936cd --- /dev/null +++ b/EXILED/Exiled.API/Features/Toys/InteractableToy.cs @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Toys +{ + using AdminToys; + using Exiled.API.Enums; + using Exiled.API.Interfaces; + using UnityEngine; + + using static AdminToys.InvisibleInteractableToy; + + /// + /// A wrapper class for . + /// + internal class InteractableToy : AdminToy, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// The of the toy. + internal InteractableToy(InvisibleInteractableToy invisibleInteractableToy) + : base(invisibleInteractableToy, AdminToyType.InvisibleInteractableToy) => Base = invisibleInteractableToy; + + /// + /// Gets the base . + /// + public InvisibleInteractableToy Base { get; } + + /// + /// Gets or sets the Shape of the Interactable. + /// + public ColliderShape Shape + { + get => Base.NetworkShape; + set => Base.NetworkShape = value; + } + + /// + /// Gets or sets the time to interact with the Interactable. + /// + public float InteractionDuration + { + get => Base.NetworkInteractionDuration; + set => Base.NetworkInteractionDuration = value; + } + + /// + /// Gets or sets a value indicating whether the interactable is locked. + /// + public bool IsLocked + { + get => Base.NetworkIsLocked; + set => Base.NetworkIsLocked = value; + } + } +} diff --git a/EXILED/Exiled.API/Features/Toys/Light.cs b/EXILED/Exiled.API/Features/Toys/Light.cs index 9496f6b833..9e167d1bb5 100644 --- a/EXILED/Exiled.API/Features/Toys/Light.cs +++ b/EXILED/Exiled.API/Features/Toys/Light.cs @@ -7,6 +7,7 @@ namespace Exiled.API.Features.Toys { + using System; using System.Linq; using AdminToys; @@ -98,6 +99,7 @@ public Color Color /// /// Gets or sets the shape that the Light emits. /// + [Obsolete("This property has been deprecated. Use LightType.Spot, LightType.Pyramid, or LightType.Box instead.")] public LightShape LightShape { get => Base.NetworkLightShape; diff --git a/EXILED/Exiled.API/Features/Toys/Speaker.cs b/EXILED/Exiled.API/Features/Toys/Speaker.cs index 2ca0b29071..859a8f2055 100644 --- a/EXILED/Exiled.API/Features/Toys/Speaker.cs +++ b/EXILED/Exiled.API/Features/Toys/Speaker.cs @@ -122,6 +122,28 @@ public static Speaker Create(Vector3? position, Vector3? rotation, Vector3? scal return speaker; } + /// + /// Creates a new . + /// + /// The transform to create this on. + /// Whether the should be initially spawned. + /// Whether the should keep the same world position. + /// The new . + public static Speaker Create(Transform transform, bool spawn, bool worldPositionStays = true) + { + Speaker speaker = new(Object.Instantiate(Prefab, transform, worldPositionStays)) + { + Position = transform.position, + Rotation = transform.rotation, + Scale = transform.localScale.normalized, + }; + + if(spawn) + speaker.Spawn(); + + return speaker; + } + /// /// Plays audio through this speaker. /// diff --git a/EXILED/Exiled.API/Features/Toys/Text.cs b/EXILED/Exiled.API/Features/Toys/Text.cs new file mode 100644 index 0000000000..7c3317cd22 --- /dev/null +++ b/EXILED/Exiled.API/Features/Toys/Text.cs @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Toys +{ + using AdminToys; + using Enums; + using Exiled.API.Interfaces; + using UnityEngine; + + /// + /// A wrapper class for . + /// + public class Text : AdminToy, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// The of the toy. + internal Text(TextToy speakerToy) + : base(speakerToy, AdminToyType.TextToy) => Base = speakerToy; + + /// + /// Gets the prefab. + /// + public static TextToy Prefab => PrefabHelper.GetPrefab(PrefabType.TextToy); + + /// + /// Gets the base . + /// + public TextToy Base { get; } + + /// + /// Gets or sets the Text shown. + /// + public string TextFormat + { + get => Base.Network_textFormat; + set => Base.Network_textFormat = value; + } + + /// + /// Gets or sets the size of the Display Size of the Text. + /// + public Vector2 DisplaySize + { + get => Base.Network_displaySize; + set => Base.Network_displaySize = value; + } + } +} diff --git a/EXILED/Exiled.API/Features/Warhead.cs b/EXILED/Exiled.API/Features/Warhead.cs index 687b4fb035..97ce5a9cfe 100644 --- a/EXILED/Exiled.API/Features/Warhead.cs +++ b/EXILED/Exiled.API/Features/Warhead.cs @@ -35,7 +35,7 @@ public static class Warhead /// /// Gets the cached component. /// - public static AlphaWarheadOutsitePanel OutsitePanel => alphaWarheadOutsitePanel != null ? alphaWarheadOutsitePanel : (alphaWarheadOutsitePanel = UnityEngine.Object.FindObjectOfType()); + public static AlphaWarheadOutsitePanel OutsitePanel => alphaWarheadOutsitePanel != null ? alphaWarheadOutsitePanel : (alphaWarheadOutsitePanel = UnityEngine.Object.FindFirstObjectByType()); /// /// Gets the of the warhead lever. @@ -84,8 +84,8 @@ public static bool LeverStatus /// public static bool IsKeycardActivated { - get => OutsitePanel.NetworkkeycardEntered; - set => OutsitePanel.NetworkkeycardEntered = value; + get => AlphaWarheadActivationPanel.IsUnlocked; + set => AlphaWarheadActivationPanel.IsUnlocked = value; } /// @@ -168,7 +168,16 @@ public static int Kills public static void CloseBlastDoors() { foreach (BlastDoor door in BlastDoors) - door.SetClosed(false, true); + door.SetDoorState(true, false); + } + + /// + /// Open the surface blast doors. + /// + public static void OpenBlastDoors() + { + foreach (BlastDoor door in BlastDoors) + door.SetDoorState(false, true); } /// @@ -186,21 +195,59 @@ public static void Start() Controller.StartDetonation(false); } + /// + /// Starts the warhead countdown. + /// + /// Indicates whether the warhead is started automatically. + /// If , subtitles will not be displayed during the countdown. + /// The of the entity that triggered the warhead. + public static void Start(bool isAutomatic, bool suppressSubtitles = false, Player trigger = null) + { + Controller.InstantPrepare(); + Controller.StartDetonation(isAutomatic, suppressSubtitles, trigger == null ? null : trigger.ReferenceHub); + } + /// /// Stops the warhead. /// public static void Stop() => Controller.CancelDetonation(); + /// + /// Stops the warhead detonation process. + /// + /// + /// The who is disabling the warhead. + /// If , the warhead will be stopped without a specific player reference. + /// + public static void Stop(Player disabler) => Controller.CancelDetonation(disabler.ReferenceHub); + /// /// Detonates the warhead. /// public static void Detonate() => Controller.ForceTime(0f); + /// + /// Detonates the warhead after the specified remaining time. + /// + /// + /// The time in seconds until the warhead detonates. + /// If set to , the warhead will detonate immediately. + /// + public static void Detonate(float remaining) => Controller.ForceTime(remaining); + /// /// Shake all players, like if the warhead has been detonated. /// public static void Shake() => Controller.RpcShake(false); + /// + /// Shake all players, like if the warhead has been detonated. + /// + /// + /// If , the shake effect will be archived. + /// + public static void Shake(bool archieve) => Controller.RpcShake(archieve); + /// /// Gets whether the provided position will be detonated by the alpha warhead. /// diff --git a/EXILED/Exiled.API/Features/Window.cs b/EXILED/Exiled.API/Features/Window.cs index a84767e1e0..892733246b 100644 --- a/EXILED/Exiled.API/Features/Window.cs +++ b/EXILED/Exiled.API/Features/Window.cs @@ -45,7 +45,7 @@ internal Window(BreakableWindow window, Room room) } /// - /// Gets a of which contains all the instances. + /// Gets a of which contains all the instances. /// public static IReadOnlyCollection List => BreakableWindowToWindow.Values; @@ -190,7 +190,7 @@ public static bool TryGet(Func predicate, out IEnumerable /// /// Break the window. /// - public void BreakWindow() => Base.BreakWindow(); + public void BreakWindow() => Base.ServerDamageWindow(Health); /// /// Damages the window. @@ -225,6 +225,7 @@ public void DamageWindow(float amount, DamageHandlerBase handler) RoomType.HczTestRoom => GlassType.TestRoom, RoomType.HczEzCheckpointA => GlassType.HczEzCheckpointA, RoomType.HczEzCheckpointB => GlassType.HczEzCheckpointB, + RoomType.Hcz127 => GlassType.Scp127, _ => GlassType.Unknown, }; } diff --git a/EXILED/Exiled.CreditTags/Exiled.CreditTags.csproj b/EXILED/Exiled.CreditTags/Exiled.CreditTags.csproj index 9382489fb3..a169d69155 100644 --- a/EXILED/Exiled.CreditTags/Exiled.CreditTags.csproj +++ b/EXILED/Exiled.CreditTags/Exiled.CreditTags.csproj @@ -27,13 +27,13 @@ + - - + diff --git a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs index 11c6a6c2a1..c2ec515742 100644 --- a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs +++ b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs @@ -17,7 +17,7 @@ namespace Exiled.CreditTags.Features public static class DatabaseHandler { - private const string Url = "https://raw.githubusercontent.com/ExMod-Team/CreditTags/main/data.yml"; + private const string Url = "https://raw.githubusercontent.com/ExSLMod-Team/CreditTags/main/data.yml"; private const string ETagCacheFileName = "etag_cache.txt"; private const string DatabaseCacheFileName = "data.yml"; private const int CacheTimeInMinutes = 5; diff --git a/EXILED/Exiled.CustomItems/API/EventArgs/OwnerEscapingEventArgs.cs b/EXILED/Exiled.CustomItems/API/EventArgs/OwnerEscapingEventArgs.cs index 2fc89ef333..54f478d7ce 100644 --- a/EXILED/Exiled.CustomItems/API/EventArgs/OwnerEscapingEventArgs.cs +++ b/EXILED/Exiled.CustomItems/API/EventArgs/OwnerEscapingEventArgs.cs @@ -30,7 +30,7 @@ public class OwnerEscapingEventArgs : EscapingEventArgs /// /// The instance. public OwnerEscapingEventArgs(Item item, EscapingEventArgs ev) - : base(ev.Player, ev.NewRole, ev.EscapeScenario) + : base(ev.Player.ReferenceHub, ev.NewRole, ev.EscapeScenario) { Item = item; } diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs b/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs index f87d473783..e6f1932905 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs @@ -8,13 +8,16 @@ namespace Exiled.CustomItems.API.Features { using System; + using System.Collections.Generic; using System.ComponentModel; using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Items; + using Exiled.API.Structs; using Exiled.Events.EventArgs.Player; + using InventorySystem.Items.Armor; using MEC; /// @@ -55,6 +58,16 @@ public override ItemType Type [Description("The value must be above 0 and below 100")] public virtual int VestEfficacy { get; set; } = 80; + /// + /// Gets or sets the Ammunition limit the player have. + /// + public virtual List AmmoLimits { get; set; } = new(); + + /// + /// Gets or sets the Item Category limit the player have. + /// + public virtual List CategoryLimits { get; set; } = new(); + /// public override void Give(Player player, bool displayMessage = true) { @@ -66,6 +79,12 @@ public override void Give(Player player, bool displayMessage = true) armor.VestEfficacy = VestEfficacy; armor.HelmetEfficacy = HelmetEfficacy; + if (AmmoLimits.Count != 0) + armor.AmmoLimits = AmmoLimits; + + if (AmmoLimits.Count != 0) + armor.CategoryLimits = CategoryLimits; + player.AddItem(armor); TrackedSerials.Add(armor.Serial); diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs index 8aff47f913..cf6d937b8b 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs @@ -17,6 +17,7 @@ namespace Exiled.CustomItems.API.Features using Exiled.API.Extensions; using Exiled.API.Features; using Exiled.API.Features.Attributes; + using Exiled.API.Features.Lockers; using Exiled.API.Features.Pickups; using Exiled.API.Features.Pools; using Exiled.API.Features.Spawn; @@ -25,15 +26,10 @@ namespace Exiled.CustomItems.API.Features using Exiled.Events.EventArgs.Player; using Exiled.Events.EventArgs.Scp914; using Exiled.Loader; - using InventorySystem.Items.Pickups; - using MEC; - using PlayerRoles; - using UnityEngine; - using YamlDotNet.Serialization; using static CustomItems; @@ -457,7 +453,7 @@ public static IEnumerable UnregisterItems() { List unregisteredItems = new(); - foreach (CustomItem customItem in Registered) + foreach (CustomItem customItem in Registered.ToList()) { customItem.TryUnregister(); unregisteredItems.Add(customItem); @@ -578,86 +574,35 @@ public virtual uint Spawn(IEnumerable spawnPoints, uint limit) if (Loader.Random.NextDouble() * 100 >= spawnPoint.Chance || (limit > 0 && spawned >= limit)) continue; - spawned++; - - /*if (spawnPoint is DynamicSpawnPoint dynamicSpawnPoint && dynamicSpawnPoint.Location == SpawnLocationType.InsideLocker) + Pickup? pickup; + if (spawnPoint is LockerSpawnPoint { UseChamber: true } lockerSpawnPoint) { - for (int i = 0; i < 50; i++) + try { - if (Exiled.API.Features.Lockers.Locker.List is null) - { - Log.Debug($"{nameof(Spawn)}: Locker list is null."); - continue; - } - - Locker locker = Exiled.API.Features.Lockers.Locker.Random(); - - 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; - - Pickup? pickup = Spawn(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 {position} ({spawnPoint.Name})"); - break; + lockerSpawnPoint.GetSpawningInfo(out _, out Chamber? chamber, out Vector3 position); + pickup = Spawn(position); + chamber?.AddItem(pickup); + } + catch (Exception e) + { + Log.Error($"CustomItem {Name}({Id} failed to spawn: {e.Message})"); + continue; } } 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})"); - }*/ - - Pickup? pickup = Spawn(spawnPoint.Position); + pickup = Spawn(spawnPoint.Position); + } if (pickup == null) continue; - if (spawnPoint is LockerSpawnPoint { UseChamber: true } lockerSpawnPoint) - { - Exiled.API.Features.Lockers.Locker? foundLocker = Exiled.API.Features.Lockers.Locker.Random(lockerSpawnPoint.Zone, lockerSpawnPoint.Type); - foundLocker?.AddItem(pickup); - } + spawned++; - if (pickup.Is(out Exiled.API.Features.Pickups.FirearmPickup firearmPickup) && this is CustomWeapon customWeapon) + /*if (pickup.Is(out FirearmPickup firearmPickup) && this is CustomWeapon customWeapon) { - // set MaxAmmo if synced TODO - } + // TODO: Set MaxAmmo (if synced) + }*/ } return spawned; diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs b/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs index e28f1ea3d3..d53846cbb0 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs @@ -8,6 +8,7 @@ namespace Exiled.CustomItems.API.Features { using System; + using System.Linq; using Exiled.API.Enums; using Exiled.API.Extensions; @@ -18,6 +19,8 @@ namespace Exiled.CustomItems.API.Features using Exiled.API.Features.Pickups; using Exiled.Events.EventArgs.Item; using Exiled.Events.EventArgs.Player; + using Interactables.Interobjects.DoorUtils; + using InventorySystem.Items.Keycards; using UnityEngine; /// @@ -33,16 +36,41 @@ public override ItemType Type set { if (!value.IsKeycard()) - throw new ArgumentOutOfRangeException("Type", value, "Invalid keycard type."); + throw new ArgumentOutOfRangeException(nameof(Type), value, "Invalid keycard type."); base.Type = value; } } + /// + /// Gets or sets name of keycard holder. + /// + public virtual string KeycardName { get; set; } = string.Empty; + + /// + /// Gets or sets a label for keycard. + /// + public virtual string KeycardLabel { get; set; } = string.Empty; + + /// + /// Gets or sets a color of keycard label. + /// + public virtual Color32? KeycardLabelColor { get; set; } + + /// + /// Gets or sets a tint color. + /// + public virtual Color32? TintColor { get; set; } + /// /// Gets or sets the permissions for custom keycard. /// - public virtual KeycardPermissions Permissions { get; set; } + public virtual KeycardPermissions Permissions { get; set; } = KeycardPermissions.None; + + /// + /// Gets or sets a color of keycard permissions. + /// + public virtual Color32? KeycardPermissionsColor { get; set; } /// public override void Give(Player player, Item item, bool displayMessage = true) @@ -50,18 +78,66 @@ public override void Give(Player player, Item item, bool displayMessage = true) base.Give(player, item, displayMessage); if (item.Is(out Keycard card)) - card.Permissions = Permissions; + SetupKeycard(card); } /// public override Pickup? Spawn(Vector3 position, Item item, Player? previousOwner = null) { if (item.Is(out Keycard card)) - card.Permissions = Permissions; + SetupKeycard(card); return base.Spawn(position, item, previousOwner); } + /// + /// Setups keycard according to this class. + /// + /// Item instance. + protected virtual void SetupKeycard(Keycard keycard) + { + if (!keycard.Base.Customizable) + return; + + DetailBase[] details = keycard.Base.Details; + + NametagDetail? nameDetail = details.OfType().FirstOrDefault(); + + if (nameDetail != null && !string.IsNullOrEmpty(KeycardName)) + NametagDetail._customNametag = KeycardName; + + CustomItemNameDetail? raNameDetail = details.OfType().FirstOrDefault(); + + if (raNameDetail != null) + raNameDetail.Name = Name; + + CustomLabelDetail? labelDetail = details.OfType().FirstOrDefault(); + + if (labelDetail != null) + { + if (!string.IsNullOrEmpty(KeycardLabel)) + CustomLabelDetail._customText = KeycardLabel; + + if (KeycardLabelColor.HasValue) + CustomLabelDetail._customColor = KeycardLabelColor.Value; + } + + CustomPermsDetail? permsDetail = details.OfType().FirstOrDefault(); + + if (permsDetail != null) + { + CustomPermsDetail._customLevels = new((DoorPermissionFlags)Permissions); + CustomPermsDetail._customColor = KeycardPermissionsColor; + } + + CustomTintDetail? tintDetail = details.OfType().FirstOrDefault(); + + if (tintDetail != null && TintColor.HasValue) + { + CustomTintDetail._customColor = TintColor.Value; + } + } + /// /// Called when custom keycard interacts with a door. /// diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs b/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs index e775ee3e9f..277037494e 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs @@ -52,7 +52,7 @@ public override ItemType Type /// /// Gets or sets the weapon damage. /// - public abstract float Damage { get; set; } + public virtual float Damage { get; set; } = -1; /// /// Gets or sets a value indicating how big of a clip the weapon will have. @@ -205,7 +205,7 @@ protected virtual void OnShot(ShotEventArgs ev) /// . protected virtual void OnHurting(HurtingEventArgs ev) { - if (ev.IsAllowed && Damage > 0f) + if (ev.IsAllowed && Damage >= 0) ev.Amount = Damage; } diff --git a/EXILED/Exiled.CustomItems/Exiled.CustomItems.csproj b/EXILED/Exiled.CustomItems/Exiled.CustomItems.csproj index 703a8a7c77..6d45708c17 100644 --- a/EXILED/Exiled.CustomItems/Exiled.CustomItems.csproj +++ b/EXILED/Exiled.CustomItems/Exiled.CustomItems.csproj @@ -28,9 +28,9 @@ + - diff --git a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs index 7d7fd8b5bf..ba6ecc95f8 100644 --- a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs +++ b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs @@ -18,15 +18,14 @@ namespace Exiled.CustomRoles.API.Features using Exiled.API.Features; using Exiled.API.Features.Attributes; using Exiled.API.Features.Pools; + using Exiled.API.Features.Roles; using Exiled.API.Features.Spawn; using Exiled.API.Interfaces; using Exiled.CustomItems.API.Features; using Exiled.Events.EventArgs.Player; using Exiled.Loader; using InventorySystem.Configs; - using MEC; - using PlayerRoles; using UnityEngine; @@ -38,6 +37,8 @@ namespace Exiled.CustomRoles.API.Features /// public abstract class CustomRole { + private const float AddRoleDelay = 0.25f; + private static Dictionary typeLookupTable = new(); private static Dictionary stringLookupTable = new(); @@ -155,6 +156,11 @@ public abstract class CustomRole /// public virtual Vector3 Scale { get; set; } = Vector3.one; + /// + /// Gets or sets a value indicating the 's gravity. + /// + public virtual Vector3? Gravity { get; set; } + /// /// Gets or sets a containing cached and their which is cached Role with FF multiplier. /// @@ -499,31 +505,31 @@ public virtual void Destroy() public virtual void AddRole(Player player) { Log.Debug($"{Name}: Adding role to {player.Nickname}."); - TrackedPlayers.Add(player); + player.UniqueRole = Name; if (Role != RoleTypeId.None) { - switch (KeepPositionOnSpawn) + if (KeepPositionOnSpawn) { - case true when KeepInventoryOnSpawn: + if (KeepInventoryOnSpawn) player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.None); - break; - case true: + else player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.AssignInventory); - break; - default: - { - if (KeepInventoryOnSpawn && player.IsAlive) - player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.UseSpawnpoint); - else - player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.All); - break; - } + } + else + { + if (KeepInventoryOnSpawn && player.IsAlive) + player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.UseSpawnpoint); + else + player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.All); } } + player.UniqueRole = Name; + TrackedPlayers.Add(player); + Timing.CallDelayed( - 0.25f, + AddRoleDelay, () => { if (!KeepInventoryOnSpawn) @@ -553,7 +559,8 @@ public virtual void AddRole(Player player) player.Health = MaxHealth; player.MaxHealth = MaxHealth; player.Scale = Scale; - + if (Gravity.HasValue && player.Role is FpcRole fpcRole) + fpcRole.Gravity = Gravity.Value; Vector3 position = GetSpawnPosition(); if (position != Vector3.zero) { @@ -574,7 +581,6 @@ public virtual void AddRole(Player player) ShowMessage(player); ShowBroadcast(player); RoleAdded(player); - player.UniqueRole = Name; player.TryAddCustomRoleFriendlyFire(Name, CustomRoleFFMultiplier); if (!string.IsNullOrEmpty(ConsoleMessage)) @@ -912,10 +918,8 @@ protected virtual void RoleRemoved(Player player) private void OnInternalChangingNickname(ChangingNicknameEventArgs ev) { - if (!Check(ev.Player)) - return; - - ev.Player.CustomInfo = $"{ev.NewName}\n{CustomInfo}"; + if (Check(ev.Player)) + ev.Player.CustomInfo = $"{ev.NewName}\n{CustomInfo}"; } private void OnInternalSpawned(SpawnedEventArgs ev) @@ -926,13 +930,8 @@ private void OnInternalSpawned(SpawnedEventArgs ev) private void OnInternalChangingRole(ChangingRoleEventArgs ev) { - if(ev.Reason == SpawnReason.Destroyed) - return; - - if (Check(ev.Player) && ((ev.NewRole == RoleTypeId.Spectator && !KeepRoleOnDeath) || (ev.NewRole != RoleTypeId.Spectator && ev.NewRole != Role && !KeepRoleOnChangingRole))) - { + if (ev.IsAllowed && ev.Reason != SpawnReason.Destroyed && Check(ev.Player) && ((ev.NewRole == RoleTypeId.Spectator && !KeepRoleOnDeath) || (ev.NewRole != RoleTypeId.Spectator && !KeepRoleOnChangingRole))) RemoveRole(ev.Player); - } } private void OnSpawningRagdoll(SpawningRagdollEventArgs ev) diff --git a/EXILED/Exiled.CustomRoles/Commands/Give.cs b/EXILED/Exiled.CustomRoles/Commands/Give.cs index 4eb357feb9..2f74b46498 100644 --- a/EXILED/Exiled.CustomRoles/Commands/Give.cs +++ b/EXILED/Exiled.CustomRoles/Commands/Give.cs @@ -19,7 +19,6 @@ namespace Exiled.CustomRoles.Commands using Exiled.Permissions.Extensions; using RemoteAdmin; - using Utils; /// /// The command to give a role to player(s). @@ -108,9 +107,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } foreach (Player player in list) - { role.AddRole(player); - } response = $"Customrole {role.Name} given to {list.Count()} players!"; diff --git a/EXILED/Exiled.CustomRoles/Exiled.CustomRoles.csproj b/EXILED/Exiled.CustomRoles/Exiled.CustomRoles.csproj index d569aacb2c..78b2030580 100644 --- a/EXILED/Exiled.CustomRoles/Exiled.CustomRoles.csproj +++ b/EXILED/Exiled.CustomRoles/Exiled.CustomRoles.csproj @@ -29,9 +29,9 @@ + - diff --git a/EXILED/Exiled.Events/Commands/Config/Merge.cs b/EXILED/Exiled.Events/Commands/Config/Merge.cs index b9916ede64..c6cf79042d 100644 --- a/EXILED/Exiled.Events/Commands/Config/Merge.cs +++ b/EXILED/Exiled.Events/Commands/Config/Merge.cs @@ -49,8 +49,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s SortedDictionary configs = ConfigManager.LoadSorted(ConfigManager.Read()); LoaderPlugin.Config.ConfigType = ConfigType.Default; bool haveBeenSaved = ConfigManager.Save(configs); - PluginAPI.Loader.AssemblyLoader.InstalledPlugins.FirstOrDefault(x => x.PluginName == "Exiled Loader")?.SaveConfig(new LoaderPlugin(), nameof(LoaderPlugin.Config)); + LoaderPlugin.Instance.SaveConfig(); response = $"Configs have been merged successfully! Feel free to remove the directory in the following path:\n\"{Paths.IndividualConfigs}\""; return haveBeenSaved; } diff --git a/EXILED/Exiled.Events/Commands/Config/Split.cs b/EXILED/Exiled.Events/Commands/Config/Split.cs index 9fad9c3e16..34816af579 100644 --- a/EXILED/Exiled.Events/Commands/Config/Split.cs +++ b/EXILED/Exiled.Events/Commands/Config/Split.cs @@ -49,8 +49,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s SortedDictionary configs = ConfigManager.LoadSorted(ConfigManager.Read()); LoaderPlugin.Config.ConfigType = ConfigType.Separated; bool haveBeenSaved = ConfigManager.Save(configs); - PluginAPI.Loader.AssemblyLoader.InstalledPlugins.FirstOrDefault(x => x.PluginName == "Exiled Loader")?.SaveConfig(new LoaderPlugin(), nameof(LoaderPlugin.Config)); + LoaderPlugin.Instance.SaveConfig(); response = $"Configs have been merged successfully! Feel free to remove the file in the following path:\n\"{Paths.Config}\""; return haveBeenSaved; } diff --git a/EXILED/Exiled.Events/Commands/Hub/Hub.cs b/EXILED/Exiled.Events/Commands/Hub/Hub.cs deleted file mode 100644 index 5df93df48c..0000000000 --- a/EXILED/Exiled.Events/Commands/Hub/Hub.cs +++ /dev/null @@ -1,51 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Commands.Hub -{ - using System; - - using CommandSystem; - - /// - /// The EXILED hub command. - /// - [CommandHandler(typeof(RemoteAdminCommandHandler))] - [CommandHandler(typeof(GameConsoleCommandHandler))] - public class Hub : ParentCommand - { - /// - /// Initializes a new instance of the class. - /// - public Hub() - { - LoadGeneratedCommands(); - } - - /// - public override string Command { get; } = "hub"; - - /// - public override string[] Aliases { get; } = Array.Empty(); - - /// - public override string Description { get; } = "The EXILED hub command."; - - /// - public override void LoadGeneratedCommands() - { - RegisterCommand(Install.Instance); - } - - /// - protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) - { - response = "Please, specify a valid subcommand! Available ones: install"; - return false; - } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Commands/Hub/HubApi/ApiProvider.cs b/EXILED/Exiled.Events/Commands/Hub/HubApi/ApiProvider.cs deleted file mode 100644 index 8733468300..0000000000 --- a/EXILED/Exiled.Events/Commands/Hub/HubApi/ApiProvider.cs +++ /dev/null @@ -1,64 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Commands.Hub.HubApi -{ - using System; - using System.IO; - using System.Net.Http; - using System.Reflection; - using System.Threading.Tasks; - - using Exiled.Events.Commands.Hub.HubApi.Models; - - using Utf8Json; - - /// - /// An API bridge to EXILED Hub. - /// - public static class ApiProvider - { - /// - /// The API endpoint to get the plugin installation data. - /// - private const string InstallApiEndpoint = "https://hub.exiled-team.net/api/install?name="; - - /// - /// Gets installation data of the plugin by name. - /// - /// The name of plugin. - /// The . - /// A instance containing installation data. - public static async Task GetInstallationData(string pluginName, HttpClient client) - { - string url = InstallApiEndpoint + pluginName; - using HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false); - - if (response.IsSuccessStatusCode) - { - using Stream stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - return JsonSerializer.Deserialize(stream); - } - - return null; - } - - /// - /// Creates a HTTP client for EXILED Hub API. - /// - /// Created HTTP client. - internal static HttpClient CreateClient() - { - HttpClient client = new(); - - client.Timeout = TimeSpan.FromSeconds(460); - client.DefaultRequestHeaders.Add("User-Agent", $"Exiled.Events (https://github.com/ExMod-Team/EXILED, {Assembly.GetExecutingAssembly().GetName().Version.ToString(3)})"); - - return client; - } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Commands/Hub/HubApi/Models/HubPlugin.cs b/EXILED/Exiled.Events/Commands/Hub/HubApi/Models/HubPlugin.cs deleted file mode 100644 index 7d0a7b8f00..0000000000 --- a/EXILED/Exiled.Events/Commands/Hub/HubApi/Models/HubPlugin.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Commands.Hub.HubApi.Models -{ - using System.Runtime.Serialization; - - using Utf8Json; - - /// - /// A struct containing all hub plugin data. - /// - public readonly struct HubPlugin : IJsonSerializable - { - /// - /// The repository id. - /// - [DataMember(Name = "repositoryId")] - public readonly long RepositoryId; - - /// - /// Initializes a new instance of the struct. - /// - /// - [SerializationConstructor] - public HubPlugin(long repositoryId) - { - RepositoryId = repositoryId; - } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Commands/Hub/Install.cs b/EXILED/Exiled.Events/Commands/Hub/Install.cs deleted file mode 100644 index 323a230108..0000000000 --- a/EXILED/Exiled.Events/Commands/Hub/Install.cs +++ /dev/null @@ -1,118 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Commands.Hub -{ - using System; - using System.IO; - using System.Linq; - using System.Net.Http; - - using CommandSystem; - - using Exiled.API.Features; - using Exiled.Events.Commands.Hub.HubApi.Models; - using Exiled.Loader; - using Exiled.Loader.GHApi; - using Exiled.Loader.GHApi.Models; - using Exiled.Loader.GHApi.Settings; - using Exiled.Permissions.Extensions; - - using RemoteAdmin; - - /// - /// The command to install a plugin from EXILED Hub. - /// - public class Install : ICommand, IUsageProvider - { - /// - /// Gets static instance of the command. - /// - public static Install Instance { get; } = new(); - - /// - public string Command { get; } = "install"; - - /// - public string[] Aliases { get; } = { "i" }; - - /// - public string[] Usage { get; } = { "Plugin name", "Release tag (optional)" }; - - /// - public string Description { get; } = "Installs a plugin from EXILED Hub."; - - /// - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - const string permission = "hub.install"; - - if (!sender.CheckPermission(permission) && sender is PlayerCommandSender playerSender && !playerSender.FullPermissions) - { - response = $"You don't have permissions to install the plugins. Required permission node: \"{permission}\"."; - return false; - } - - if (arguments.Count == 0) - { - response = "Missing arguments! Usage: hub install (release tag)"; - return false; - } - - using HttpClient client = HubApi.ApiProvider.CreateClient(); - - HubPlugin? pluginData = HubApi.ApiProvider.GetInstallationData(arguments.At(0), client).GetAwaiter().GetResult(); - - if (pluginData == null) - { - response = "An error has occurred while fetching the plugin data. Please check if the plugin name is correct and try again."; - return false; - } - - Release[] pluginReleases = client.GetReleases(pluginData.Value.RepositoryId, new GetReleasesSettings(50, 1)).GetAwaiter().GetResult(); - Release releaseToDownload = pluginReleases[0]; - - if (arguments.Count > 1) - { - Release foundRelease = pluginReleases.FirstOrDefault(x => x.TagName == arguments.At(1)); - - if (foundRelease.Id == 0) - { - response = "Release with the provided tag not found."; - return false; - } - - releaseToDownload = foundRelease; - } - - ReleaseAsset[] releaseAssets = releaseToDownload.Assets.Where(x => x.Name.IndexOf("nwapi", StringComparison.OrdinalIgnoreCase) == -1).ToArray(); - - Log.Info($"Downloading release \"{releaseToDownload.TagName}\". Found {releaseAssets.Length} asset(s) to download."); - - foreach (ReleaseAsset asset in releaseAssets) - { - Log.Info($"Downloading asset {asset.Name}. Asset size: {Math.Round(asset.Size / 1000f, 2)} KB."); - using HttpResponseMessage assetResponse = client.GetAsync(asset.BrowserDownloadUrl).ConfigureAwait(false).GetAwaiter().GetResult(); - - string pluginPath = Path.Combine(Paths.Plugins, asset.Name); - - if (File.Exists(pluginPath) && Environment.OSVersion.Platform == PlatformID.Unix) - LinuxPermission.SetFileUserAndGroupReadWriteExecutePermissions(pluginPath); - - using Stream stream = assetResponse.Content.ReadAsStreamAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - using FileStream fileStream = new(pluginPath, FileMode.Create, FileAccess.Write, FileShare.None); - stream.CopyToAsync(fileStream).ConfigureAwait(false).GetAwaiter().GetResult(); - - if (Environment.OSVersion.Platform == PlatformID.Unix) - LinuxPermission.SetFileUserAndGroupReadWriteExecutePermissions(pluginPath); - } - - response = $"{arguments.At(0)} has been successfully installed."; - return true; - } - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Config.cs b/EXILED/Exiled.Events/Config.cs index 5559774efa..fd244f699b 100644 --- a/EXILED/Exiled.Events/Config.cs +++ b/EXILED/Exiled.Events/Config.cs @@ -44,12 +44,6 @@ public sealed class Config : IConfig [Description("Indicates whether SCP-049 can sense tutorial players")] public bool CanScp049SenseTutorial { get; set; } = true; - /// - /// Gets or sets a value indicating whether Tutorial is affected by Scp-939 Amnestic Cloud. - /// - [Description("Indicates whether Tutorial is affected by Scp-939 Amnestic Cloud")] - public bool TutorialAffectedByScp939AmnesticCloud { get; set; } = true; - /// /// Gets or sets a value indicating whether tutorial is affected by SCP-079 scan. /// @@ -83,6 +77,7 @@ public sealed class Config : IConfig /// /// Gets or sets a value indicating whether keycard throw can affect basic doors. /// + /// TODO: Make a poll about removing this config. (unimplemented since 9.6.0-beta7) [Description("Indicates whether thrown keycards can affect doors that don't require any permissions")] public bool CanKeycardThrowAffectDoors { get; set; } = false; diff --git a/EXILED/Exiled.Events/EventArgs/Cassie/SendingCassieMessageEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Cassie/SendingCassieMessageEventArgs.cs index e39a463c23..a8b986c171 100644 --- a/EXILED/Exiled.Events/EventArgs/Cassie/SendingCassieMessageEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Cassie/SendingCassieMessageEventArgs.cs @@ -26,12 +26,20 @@ public class SendingCassieMessageEventArgs : IDeniableEvent /// /// /// + /// + /// + /// + /// + /// + /// /// Indicates whether the event can be executed. - public SendingCassieMessageEventArgs(string words, bool makeHold, bool makeNoise, bool isAllowed = true) + public SendingCassieMessageEventArgs(string words, bool makeHold, bool makeNoise, bool customAnnouncement, string customSubtitles, bool isAllowed = true) { Words = words; + CustomSubtitles = customSubtitles; MakeHold = makeHold; MakeNoise = makeNoise; + IsCustomAnnouncement = customAnnouncement; IsAllowed = isAllowed; } @@ -40,6 +48,11 @@ public SendingCassieMessageEventArgs(string words, bool makeHold, bool makeNoise /// public string Words { get; set; } + /// + /// Gets or sets the message subtitles. + /// + public string CustomSubtitles { get; set; } + /// /// Gets or sets a value indicating whether the message should be held. /// @@ -54,5 +67,10 @@ public SendingCassieMessageEventArgs(string words, bool makeHold, bool makeNoise /// Gets or sets a value indicating whether the message can be sent. /// public bool IsAllowed { get; set; } + + /// + /// Gets or sets a value indicating whether the message can be sent. + /// + public bool IsCustomAnnouncement { get; set; } } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IMicroHIDEvent.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IMicroHIDEvent.cs new file mode 100644 index 0000000000..bc17988cc2 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IMicroHIDEvent.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Interfaces +{ + using API.Features.Items; + + /// + /// Event args used for all related events. + /// + public interface IMicroHIDEvent : IItemEvent + { + /// + /// Gets the triggering the event. + /// + public MicroHid MicroHID { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Item/DisruptorFiringEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/DisruptorFiringEventArgs.cs new file mode 100644 index 0000000000..ad3feef017 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Item/DisruptorFiringEventArgs.cs @@ -0,0 +1,56 @@ +// ----------------DissolveMatPool------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Item +{ + using Exiled.API.Features.Pickups; + using Interfaces; + using InventorySystem.Items.Firearms.Modules; + using InventorySystem.Items.Pickups; + + /// + /// Contains all information before a pickup shoot while on the ground. + /// + public class DisruptorFiringEventArgs : IDeniableEvent, IPickupEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public DisruptorFiringEventArgs(Pickup disruptor, API.Features.Player attacker, DisruptorActionModule.FiringState state, bool isAllowed = true) + { + Pickup = disruptor; + Attacker = attacker; + State = state; + IsAllowed = isAllowed; + } + + /// + /// Gets or Sets a value indicating whether the disruptor shoot and the ground. + /// The client will still see all effects, like sounds and shoot. + /// + public bool IsAllowed { get; set; } + + /// + /// Gets or Sets whether is the attacker. + /// + public API.Features.Player Attacker { get; set; } + + /// + /// Gets the state of the weapon. + /// + public DisruptorActionModule.FiringState State { get; } + + /// + /// Gets the pickup who shot the bullet. + /// + public Pickup Pickup { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs index a083ac1fa1..4c43eb1df8 100644 --- a/EXILED/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Map/AnnouncingScpTerminationEventArgs.cs @@ -30,17 +30,14 @@ public class AnnouncingScpTerminationEventArgs : IAttackerEvent, IDeniableEvent /// /// /// - /// - /// - /// - public AnnouncingScpTerminationEventArgs(Player scp, DamageHandlerBase damageHandlerBase, bool isAllowed = true) + public AnnouncingScpTerminationEventArgs(Player scp, DamageHandlerBase damageHandlerBase) { Player = scp; Role = scp.Role; DamageHandler = new CustomDamageHandler(scp, damageHandlerBase); Attacker = DamageHandler.BaseIs(out CustomAttackerHandler customAttackerHandler) ? customAttackerHandler.Attacker : null; TerminationCause = damageHandlerBase.CassieDeathAnnouncement.Announcement; - IsAllowed = isAllowed; + IsAllowed = true; } /// @@ -71,6 +68,6 @@ public AnnouncingScpTerminationEventArgs(Player scp, DamageHandlerBase damageHan /// /// Gets or sets a value indicating whether the SCP termination will be announced by C.A.S.S.I.E. /// - public bool IsAllowed { get; set; } = true; + public bool IsAllowed { get; set; } } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs index 66043379d5..205500cf60 100644 --- a/EXILED/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs @@ -41,7 +41,7 @@ public FillingLockerEventArgs(ItemPickupBase pickupBase, LockerChamber lockerCha /// /// Gets a locker which is containing . /// - public API.Features.Lockers.Locker Locker => Chamber.Locker; + public API.Features.Lockers.Locker Locker => Chamber?.Locker; /// /// Gets a chamber which is filling. diff --git a/EXILED/Exiled.Events/EventArgs/Map/SpawningRoomConnectorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/SpawningRoomConnectorEventArgs.cs new file mode 100644 index 0000000000..179c244e83 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Map/SpawningRoomConnectorEventArgs.cs @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Map +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using MapGeneration.RoomConnectors; + using MapGeneration.RoomConnectors.Spawners; + + /// + /// Contains all information before spawning the connector between rooms. + /// + public class SpawningRoomConnectorEventArgs : IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// The RoomConnectorSpawnpointBase. + /// The connector type the game is trying to spawn. + public SpawningRoomConnectorEventArgs(RoomConnectorSpawnpointBase roomConnectorSpawnpointBase, SpawnableRoomConnectorType connectorType) + { + RoomConnectorSpawnpoint = roomConnectorSpawnpointBase; + ConnectorType = connectorType; + RoomForward = Room.Get(RoomConnectorSpawnpoint._parentRoom); + RoomBackward = Room.Get(RoomConnectorSpawnpoint.transform.position + (RoomConnectorSpawnpoint.transform.forward * -1)); + } + + /// + /// Gets the RoomConnectorSpawnpointBase. + /// + public RoomConnectorSpawnpointBase RoomConnectorSpawnpoint { get; } + + /// + /// Gets the Room forward of the Connector. + /// + public Room RoomForward { get; } + + /// + /// Gets the Room Backward of the Connector. + /// + public Room RoomBackward { get; } + + /// + /// Gets or sets which Connector the game should spawn. + /// + public SpawnableRoomConnectorType ConnectorType { get; set; } + + /// + /// Gets or sets a value indicating whether the connector can be spawned. + /// + public bool IsAllowed { get; set; } = true; + } +} diff --git a/EXILED/Exiled.Events/EventArgs/Player/ActivatingGeneratorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ActivatingGeneratorEventArgs.cs index 33a6e87ee5..8050e534bf 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ActivatingGeneratorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ActivatingGeneratorEventArgs.cs @@ -27,14 +27,11 @@ public class ActivatingGeneratorEventArgs : IPlayerEvent, IGeneratorEvent, IDeni /// /// /// - /// - /// - /// - public ActivatingGeneratorEventArgs(Player player, Scp079Generator generator, bool isAllowed = true) + public ActivatingGeneratorEventArgs(Player player, Scp079Generator generator) { Player = player; Generator = Generator.Get(generator); - IsAllowed = isAllowed; + IsAllowed = true; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/ActivatingWarheadPanelEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ActivatingWarheadPanelEventArgs.cs index f31eded385..65504085ed 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ActivatingWarheadPanelEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ActivatingWarheadPanelEventArgs.cs @@ -25,7 +25,7 @@ public class ActivatingWarheadPanelEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - public ActivatingWarheadPanelEventArgs(Player player, bool isAllowed = true) + public ActivatingWarheadPanelEventArgs(Player player, bool isAllowed) { Player = player; IsAllowed = isAllowed; diff --git a/EXILED/Exiled.Events/EventArgs/Player/CancelledItemUseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/CancelledItemUseEventArgs.cs index 33719e6217..19435c5250 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/CancelledItemUseEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/CancelledItemUseEventArgs.cs @@ -10,6 +10,7 @@ namespace Exiled.Events.EventArgs.Player using API.Features; using Exiled.API.Features.Items; using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items.Usables; /// /// Contains all information before a player cancels usage of an item. @@ -19,14 +20,16 @@ public class CancelledItemUseEventArgs : IPlayerEvent, IUsableEvent /// /// Initializes a new instance of the class. /// - /// The player who's stopping the use of an item. - /// + /// + /// + /// + /// /// /// - public CancelledItemUseEventArgs(Player player, Item item) + public CancelledItemUseEventArgs(ReferenceHub hub, UsableItem usableItem) { - Player = player; - Usable = item is Usable usable ? usable : null; + Player = Player.Get(hub); + Usable = Item.Get(usableItem); } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs index 1caaabb0a6..a030f171c2 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs @@ -20,13 +20,15 @@ public class CancellingItemUseEventArgs : IPlayerEvent, IDeniableEvent, IUsableE /// /// Initializes a new instance of the class. /// - /// The player who's stopping the use of an item. + /// + /// + /// /// /// /// - public CancellingItemUseEventArgs(Player player, UsableItem item) + public CancellingItemUseEventArgs(ReferenceHub hub, UsableItem item) { - Player = player; + Player = Player.Get(hub); Usable = Item.Get(item); } diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs index 2881241cdd..3e3c1eee9c 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs @@ -20,7 +20,7 @@ namespace Exiled.Events.EventArgs.Player /// /// Contains all information before MicroHID state is changed. /// - public class ChangingMicroHIDStateEventArgs : IDeniableEvent, IItemEvent + public class ChangingMicroHIDStateEventArgs : IDeniableEvent, IMicroHIDEvent { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.Events/EventArgs/Player/ClosingGeneratorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ClosingGeneratorEventArgs.cs index 9218db494b..c33b0bcc0f 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ClosingGeneratorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ClosingGeneratorEventArgs.cs @@ -21,12 +21,11 @@ public class ClosingGeneratorEventArgs : IPlayerEvent, IDeniableEvent, IGenerato /// /// The player who's closing the generator. /// The instance. - /// Indicates whether the generator can be closed. - public ClosingGeneratorEventArgs(Player player, Scp079Generator generator, bool isAllowed = true) + public ClosingGeneratorEventArgs(Player player, Scp079Generator generator) { Player = player; Generator = Generator.Get(generator); - IsAllowed = isAllowed; + IsAllowed = true; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/DeactivatingWorkstationEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DeactivatingWorkstationEventArgs.cs index 48b7164d83..20ce6f84d0 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/DeactivatingWorkstationEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/DeactivatingWorkstationEventArgs.cs @@ -31,7 +31,7 @@ public class DeactivatingWorkstationEventArgs : IPlayerEvent, IDeniableEvent /// public DeactivatingWorkstationEventArgs(WorkstationController controller, bool isAllowed = true) { - Player = Player.Get(controller._knownUser); + Player = Player.Get(controller.KnownUser); WorkstationController = controller; IsAllowed = isAllowed; } diff --git a/EXILED/Exiled.Events/EventArgs/Player/EscapingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/EscapingEventArgs.cs index a10eea6922..4a4fa0dcba 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/EscapingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/EscapingEventArgs.cs @@ -25,7 +25,7 @@ public class EscapingEventArgs : IPlayerEvent, IDeniableEvent /// /// Initializes a new instance of the class. /// - /// + /// /// /// /// @@ -34,12 +34,12 @@ public class EscapingEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - public EscapingEventArgs(Player player, RoleTypeId newRole, EscapeScenario escapeScenario) + public EscapingEventArgs(ReferenceHub referenceHub, RoleTypeId newRole, EscapeScenario escapeScenario) { - Player = player; + Player = Player.Get(referenceHub); NewRole = newRole; EscapeScenario = escapeScenario; - IsAllowed = escapeScenario is not EscapeScenario.CustomEscape; + IsAllowed = escapeScenario is not EscapeScenario.None and not EscapeScenario.CustomEscape; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/EscapingPocketDimensionEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/EscapingPocketDimensionEventArgs.cs index 375eed5302..841edfdcf6 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/EscapingPocketDimensionEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/EscapingPocketDimensionEventArgs.cs @@ -21,18 +21,27 @@ public class EscapingPocketDimensionEventArgs : IPlayerEvent, IDeniableEvent /// /// Initializes a new instance of the class. /// - /// + /// + /// + /// + /// /// /// /// /// /// - public EscapingPocketDimensionEventArgs(Player player, Vector3 position) + public EscapingPocketDimensionEventArgs(PocketDimensionTeleport pocketDimensionTeleport, ReferenceHub hub, Vector3 position) { - Player = player; + Teleporter = pocketDimensionTeleport; + Player = Player.Get(hub); TeleportPosition = position; } + /// + /// Gets the PocketDimensionTeleport the player walked into. + /// + public PocketDimensionTeleport Teleporter { get; } + /// /// Gets the player who's escaping the pocket dimension. /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/ExplodingMicroHIDEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ExplodingMicroHIDEventArgs.cs new file mode 100644 index 0000000000..c2727317aa --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/ExplodingMicroHIDEventArgs.cs @@ -0,0 +1,51 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items.MicroHID; + + /// + /// Contains all information before the micro hid explode. + /// + public class ExplodingMicroHIDEventArgs : IDeniableEvent, IMicroHIDEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Whether the Micro HID can explode the player or not. + public ExplodingMicroHIDEventArgs(MicroHIDItem item, bool isAllowed = true) + { + MicroHID = Item.Get(item); + Player = MicroHID.Owner; + IsAllowed = isAllowed; + } + + /// + /// Gets the item. + /// + public Item Item => MicroHID; + + /// + /// Gets the player in owner of the item. + /// + public Exiled.API.Features.Player Player { get; } + + /// + /// Gets MicroHid item. + /// + public MicroHid MicroHID { get; } + + /// + /// Gets or sets a value indicating whether the player can explode the micro HID. + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/FailingEscapePocketDimensionEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/FailingEscapePocketDimensionEventArgs.cs index 247e62cbe7..620178368c 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/FailingEscapePocketDimensionEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/FailingEscapePocketDimensionEventArgs.cs @@ -7,6 +7,8 @@ namespace Exiled.Events.EventArgs.Player { + using System; + using API.Features; using Interfaces; @@ -19,19 +21,19 @@ public class FailingEscapePocketDimensionEventArgs : IPlayerEvent, IDeniableEven /// /// Initializes a new instance of the class. /// - /// - /// - /// - /// + /// /// /// + /// + /// + /// /// /// /// - public FailingEscapePocketDimensionEventArgs(Player player, PocketDimensionTeleport teleporter, bool isAllowed = true) + public FailingEscapePocketDimensionEventArgs(PocketDimensionTeleport pocketDimensionTeleport, ReferenceHub hub, bool isAllowed = true) { - Player = player; - Teleporter = teleporter; + Player = Player.Get(hub); + Teleporter = pocketDimensionTeleport; IsAllowed = isAllowed; } diff --git a/EXILED/Exiled.Events/EventArgs/Player/FlippingCoinEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/FlippingCoinEventArgs.cs index 369a443868..954d2f39e8 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/FlippingCoinEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/FlippingCoinEventArgs.cs @@ -20,7 +20,7 @@ public class FlippingCoinEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent /// /// Initializes a new instance of the class. /// - /// + /// /// /// /// @@ -29,9 +29,9 @@ public class FlippingCoinEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent /// /// /// - public FlippingCoinEventArgs(Player player, Coin coin, bool isTails) + public FlippingCoinEventArgs(ReferenceHub referenceHub, Coin coin, bool isTails) { - Player = player; + Player = Player.Get(referenceHub); Item = Item.Get(coin); IsTails = isTails; } diff --git a/EXILED/Exiled.Events/EventArgs/Player/HitEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/HitEventArgs.cs new file mode 100644 index 0000000000..d9d18adc0e --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/HitEventArgs.cs @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using System.Collections.Generic; + using System.Linq; + + using API.Features; + using Interfaces; + using PlayerRoles.PlayableScps.Subroutines; + + /// + /// Contains all information after player sends an attack as an SCP. + /// + public class HitEventArgs : IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// the result of the attack. + /// the list of players who are getting hit. + public HitEventArgs(Player player, AttackResult result, HashSet playerHits) + { + Player = player; + Result = result; + PlayersAffected = playerHits.Select(Player.Get).ToList().AsReadOnly(); + } + + /// + public Player Player { get; } + + /// + /// Gets the attack result for the server. + /// + public AttackResult Result { get; } + + /// + /// Gets the attack result for the server. + /// + public IReadOnlyCollection PlayersAffected { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/HurtEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/HurtEventArgs.cs index 1b97b4a946..8fd0eab99b 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/HurtEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/HurtEventArgs.cs @@ -23,7 +23,7 @@ public class HurtEventArgs : IAttackerEvent /// /// Initializes a new instance of the class. /// - /// + /// /// /// /// @@ -32,11 +32,11 @@ public class HurtEventArgs : IAttackerEvent /// /// /// - public HurtEventArgs(Player target, DamageHandlerBase damageHandler, DamageHandlerBase.HandlerOutput handlerOutput) + public HurtEventArgs(ReferenceHub referenceHub, DamageHandlerBase damageHandler, DamageHandlerBase.HandlerOutput handlerOutput) { - DamageHandler = new CustomDamageHandler(target, damageHandler); + Player = Player.Get(referenceHub); + DamageHandler = new CustomDamageHandler(Player, damageHandler); Attacker = DamageHandler.BaseIs(out CustomAttackerHandler attackerDamageHandler) ? attackerDamageHandler.Attacker : null; - Player = target; HandlerOutput = handlerOutput; } diff --git a/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs index 75eb562f39..c93656d6fc 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/InteractingDoorEventArgs.cs @@ -33,17 +33,14 @@ public class InteractingDoorEventArgs : IPlayerEvent, IDoorEvent, IDeniableEvent /// /// /// - /// - /// - /// - public InteractingDoorEventArgs(Player player, DoorVariant door, byte colliderId, bool isAllowed = true, bool canInteract = true) + public InteractingDoorEventArgs(Player player, DoorVariant door, byte colliderId, bool isAllowed) { Player = player; Door = Door.Get(door); ColliderId = colliderId; Collider = InteractableCollider.TryGetCollider(door, colliderId, out InteractableCollider interactableCollider) ? interactableCollider : null; IsAllowed = isAllowed; - CanInteract = canInteract; + CanInteract = true; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/MicroHIDOpeningDoorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/MicroHIDOpeningDoorEventArgs.cs new file mode 100644 index 0000000000..36ff69e9b1 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/MicroHIDOpeningDoorEventArgs.cs @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features.Doors; + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Interfaces; + + using Interactables.Interobjects.DoorUtils; + using InventorySystem.Items.MicroHID; + + /// + /// Contains all information before the micro opens a doors. + /// + public class MicroHIDOpeningDoorEventArgs : IDeniableEvent, IDoorEvent, IMicroHIDEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Whether the Micro HID can open the door or not. + /// + public MicroHIDOpeningDoorEventArgs(MicroHIDItem item, DoorVariant door, bool isAllowed = true) + { + MicroHID = Item.Get(item); + Player = MicroHID.Owner; + IsAllowed = isAllowed; + Door = Door.Get(door); + } + + /// + /// Gets the item. + /// + public Item Item => MicroHID; + + /// + /// Gets the player in owner of the item. + /// + public Exiled.API.Features.Player Player { get; } + + /// + /// Gets MicroHid item. + /// + public MicroHid MicroHID { get; } + + /// + /// Gets the instance. + /// + public Door Door { get; } + + /// + /// Gets or sets a value indicating whether the player can explode the micro HID. + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/OpeningGeneratorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/OpeningGeneratorEventArgs.cs index f69507e355..13dc43febf 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/OpeningGeneratorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/OpeningGeneratorEventArgs.cs @@ -27,14 +27,11 @@ public class OpeningGeneratorEventArgs : IPlayerEvent, IDeniableEvent, IGenerato /// /// /// - /// - /// - /// - public OpeningGeneratorEventArgs(Player player, Scp079Generator generator, bool isAllowed = true) + public OpeningGeneratorEventArgs(Player player, Scp079Generator generator) { Player = player; Generator = Generator.Get(generator); - IsAllowed = isAllowed; + IsAllowed = true; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/PickingUpItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/PickingUpItemEventArgs.cs index 90493c03c5..f71781e3b7 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/PickingUpItemEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/PickingUpItemEventArgs.cs @@ -21,7 +21,7 @@ public class PickingUpItemEventArgs : IPlayerEvent, IPickupEvent, IDeniableEvent /// /// Initializes a new instance of the class. /// - /// + /// /// /// /// @@ -30,10 +30,10 @@ public class PickingUpItemEventArgs : IPlayerEvent, IPickupEvent, IDeniableEvent /// /// /// - public PickingUpItemEventArgs(Player player, ItemPickupBase pickup, bool isAllowed = true) + public PickingUpItemEventArgs(ReferenceHub referenceHub, ItemPickupBase pickup, bool isAllowed = true) { IsAllowed = isAllowed; - Player = player; + Player = Player.Get(referenceHub); Pickup = Pickup.Get(pickup); } diff --git a/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs index f2af6a8300..3c127ca02e 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/PreAuthenticatingEventArgs.cs @@ -13,7 +13,6 @@ namespace Exiled.Events.EventArgs.Player using LiteNetLib; using LiteNetLib.Utils; - using PluginAPI.Events; #pragma warning disable SA1600 //TODO: #pragma warning disable SA1309 diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReservedSlotsCheckEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReservedSlotsCheckEventArgs.cs index 47861a038c..d571f1e451 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ReservedSlotsCheckEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ReservedSlotsCheckEventArgs.cs @@ -13,21 +13,24 @@ namespace Exiled.Events.EventArgs.Player /// /// Contains all information when checking if a player has a reserved slot. /// - public class ReservedSlotsCheckEventArgs : IExiledEvent + public class ReservedSlotsCheckEventArgs : IExiledEvent, IDeniableEvent { + private ReservedSlotEventResult reservedSlotEventResult = ReservedSlotEventResult.UseBaseGameSystem; + /// /// Initializes a new instance of the class. /// - /// - /// - /// /// /// /// - public ReservedSlotsCheckEventArgs(string userId, bool hasReservedSlot) + /// + /// + /// + public ReservedSlotsCheckEventArgs(bool hasReservedSlot, string userId) { UserId = userId; HasReservedSlot = hasReservedSlot; + IsAllowed = hasReservedSlot; } /// @@ -40,9 +43,36 @@ public ReservedSlotsCheckEventArgs(string userId, bool hasReservedSlot) /// public bool HasReservedSlot { get; } + /// + /// Gets or sets a value indicating whether the player is allowed to connect. + /// + public bool IsAllowed { get; set; } + /// /// Gets or sets the event result. /// - public ReservedSlotEventResult Result { get; set; } = ReservedSlotEventResult.UseBaseGameSystem; + public ReservedSlotEventResult Result + { + get => reservedSlotEventResult; + set + { + switch (reservedSlotEventResult) + { + case ReservedSlotEventResult.CanUseReservedSlots or ReservedSlotEventResult.UseBaseGameSystem: + IsAllowed = HasReservedSlot; + break; + case ReservedSlotEventResult.AllowConnectionUnconditionally: + IsAllowed = true; + break; + case ReservedSlotEventResult.CannotUseReservedSlots: + IsAllowed = false; + break; + default: + return; + } + + reservedSlotEventResult = value; + } + } } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs index c714df950a..fe17eb979a 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/SearchingPickupEventArgs.cs @@ -37,7 +37,7 @@ public class SearchingPickupEventArgs : IPlayerEvent, IPickupEvent, IDeniableEve /// /// /// - public SearchingPickupEventArgs(Player player, ItemPickupBase pickup, SearchSession searchSession, SearchCompletor searchCompletor, float searchTime) + public SearchingPickupEventArgs(Player player, ItemPickupBase pickup, SearchSession searchSession, PickupSearchCompletor searchCompletor, float searchTime) { Player = player; Pickup = Pickup.Get(pickup); @@ -54,7 +54,7 @@ public SearchingPickupEventArgs(Player player, ItemPickupBase pickup, SearchSess /// /// Gets or sets the SearchCompletor. /// - public SearchCompletor SearchCompletor { get; set; } + public PickupSearchCompletor SearchCompletor { get; set; } /// /// Gets or sets the Pickup search duration. diff --git a/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs index a3fa2d5310..1ea8a6df71 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/SendingValidCommandEventArgs.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.EventArgs.Player using Exiled.API.Features; using Exiled.API.Features.Pickups; using Exiled.Events.EventArgs.Interfaces; - using PluginAPI.Enums; + using LabApi.Features.Enums; using RemoteAdmin; /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs index 0fb7778a16..1c96ff88f9 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/SentValidCommandEventArgs.cs @@ -11,7 +11,7 @@ namespace Exiled.Events.EventArgs.Player using Exiled.API.Features; using Exiled.API.Features.Pickups; using Exiled.Events.EventArgs.Interfaces; - using PluginAPI.Enums; + using LabApi.Features.Enums; using RemoteAdmin; /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs index 0eb6a5c69d..18d2303353 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs @@ -25,15 +25,16 @@ public class ShotEventArgs : IPlayerEvent, IFirearmEvent /// Raycast hit info. /// The firearm used. /// The IDestructible that was hit. Can be null. - public ShotEventArgs(HitscanHitregModuleBase hitregModule, RaycastHit hitInfo, InventorySystem.Items.Firearms.Firearm firearm, IDestructible destructible) + /// + public ShotEventArgs(HitscanHitregModuleBase hitregModule, RaycastHit hitInfo, InventorySystem.Items.Firearms.Firearm firearm, IDestructible destructible, float damage) { HitregModule = hitregModule; RaycastHit = hitInfo; Destructible = destructible; Firearm = Item.Get(firearm); + Damage = damage; Player = Firearm.Owner; - Damage = Destructible is not null ? HitregModule.DamageAtDistance(hitInfo.distance) : 0f; if (Destructible is HitboxIdentity hitboxIdentity) { diff --git a/EXILED/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs index e12dc914a1..9ec7423835 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/SpawningRagdollEventArgs.cs @@ -8,7 +8,6 @@ namespace Exiled.Events.EventArgs.Player { using API.Features; - using Interfaces; using PlayerRoles; @@ -16,6 +15,7 @@ namespace Exiled.Events.EventArgs.Player using PlayerStatsSystem; using UnityEngine; + using YamlDotNet.Core.Tokens; /// /// Contains all information before spawning a player ragdoll. @@ -45,7 +45,7 @@ public SpawningRagdollEventArgs(RagdollData info, bool isAllowed = true) public Vector3 Position { get => Info.StartPosition; - set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, value, Rotation); + set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, Role, value, Rotation, Scale, Nickname, CreationTime); } /// @@ -54,13 +54,22 @@ public Vector3 Position public Quaternion Rotation { get => Info.StartRotation; - set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, Position, value); + set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, Role, Position, value, Scale, Nickname, CreationTime); + } + + /// + /// Gets or sets the ragdoll's scale with RagdollData. + /// + public Vector3 Scale + { + get => Info.Scale; + set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, Role, Position, Rotation, Vector3.Scale(value, RagdollManager.GetDefaultScale(Role)), Nickname, CreationTime); } /// - /// Gets or sets the ragdoll's scale. + /// Gets or sets the ragdoll's scale with GameObject. /// - public Vector3 Scale { get; set; } + public Vector3 RagdollScale { get; set; } = Vector3.one; /// /// Gets or sets the ragdoll's . @@ -68,7 +77,7 @@ public Quaternion Rotation public RoleTypeId Role { get => Info.RoleType; - set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, value, Position, Rotation, Nickname, CreationTime); + set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, value, Position, Rotation, Scale, Nickname, CreationTime); } /// @@ -82,7 +91,7 @@ public RoleTypeId Role public string Nickname { get => Info.Nickname; - set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, Role, Position, Rotation, value, CreationTime); + set => Info = new RagdollData(Player.ReferenceHub, DamageHandlerBase, Role, Position, Rotation, Scale, value, CreationTime); } /// @@ -96,7 +105,7 @@ public string Nickname public DamageHandlerBase DamageHandlerBase { get => Info.Handler; - set => Info = new RagdollData(Player.ReferenceHub, value, Role, Position, Rotation, Nickname, CreationTime); + set => Info = new RagdollData(Player.ReferenceHub, value, Role, Position, Rotation, Scale, Nickname, CreationTime); } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/StoppingGeneratorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/StoppingGeneratorEventArgs.cs index 53f0d9ecf5..6462c655d8 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/StoppingGeneratorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/StoppingGeneratorEventArgs.cs @@ -21,12 +21,11 @@ public class StoppingGeneratorEventArgs : IPlayerEvent, IGeneratorEvent, IDeniab /// /// The player who's flipping the switch. /// The instance. - /// Indicates whether the switch of the generator can be flipped. - public StoppingGeneratorEventArgs(Player player, Scp079Generator generator, bool isAllowed = true) + public StoppingGeneratorEventArgs(Player player, Scp079Generator generator) { Player = player; Generator = Generator.Get(generator); - IsAllowed = isAllowed; + IsAllowed = true; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingNoClipEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingNoClipEventArgs.cs index 78144567ec..65ea273731 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/TogglingNoClipEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingNoClipEventArgs.cs @@ -19,7 +19,7 @@ public class TogglingNoClipEventArgs : IPlayerEvent, IDeniableEvent /// /// Initializes a new instance of the class. /// - /// + /// /// /// /// @@ -28,9 +28,9 @@ public class TogglingNoClipEventArgs : IPlayerEvent, IDeniableEvent /// /// /// - public TogglingNoClipEventArgs(Player player, bool newValue, bool isAllowed = true) + public TogglingNoClipEventArgs(ReferenceHub referenceHub, bool newValue, bool isAllowed = true) { - Player = player; + Player = Player.Get(referenceHub); IsEnabled = newValue; IsAllowed = isAllowed; } @@ -41,9 +41,9 @@ public TogglingNoClipEventArgs(Player player, bool newValue, bool isAllowed = tr public Player Player { get; } /// - /// Gets or sets a value indicating whether the noclip mode will be enabled. + /// Gets a value indicating whether the noclip mode will be enabled. /// - public bool IsEnabled { get; set; } + public bool IsEnabled { get; } /// /// Gets or sets a value indicating whether the player can toggle noclip. diff --git a/EXILED/Exiled.Events/EventArgs/Player/UnlockingGeneratorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UnlockingGeneratorEventArgs.cs index 422def506f..77541732d0 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/UnlockingGeneratorEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/UnlockingGeneratorEventArgs.cs @@ -30,7 +30,7 @@ public class UnlockingGeneratorEventArgs : IPlayerEvent, IGeneratorEvent, IDenia /// /// /// - public UnlockingGeneratorEventArgs(Player player, Scp079Generator generator, bool isAllowed = true) + public UnlockingGeneratorEventArgs(Player player, Scp079Generator generator, bool isAllowed) { Player = player; Generator = Generator.Get(generator); diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingItemEventArgs.cs index 6165eb35c3..317d6ddbeb 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/UsingItemEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/UsingItemEventArgs.cs @@ -21,16 +21,18 @@ public class UsingItemEventArgs : IPlayerEvent, IDeniableEvent, IUsableEvent /// /// Initializes a new instance of the class. /// - /// The player who's going to use the item. + /// + /// + /// /// /// /// /// /// /// - public UsingItemEventArgs(Player player, UsableItem item, float cooldown) + public UsingItemEventArgs(ReferenceHub hub, UsableItem item, float cooldown) { - Player = player; + Player = Player.Get(hub); Usable = Item.Get(item) is Usable usable ? usable : null; Cooldown = cooldown; } diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs index cbf292d86f..2cfe7fee35 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs @@ -17,7 +17,7 @@ namespace Exiled.Events.EventArgs.Player /// /// Contains all information before MicroHID energy is changed. /// - public class UsingMicroHIDEnergyEventArgs : IDeniableEvent, IItemEvent + public class UsingMicroHIDEnergyEventArgs : IDeniableEvent, IMicroHIDEvent { /// /// Initializes a new instance of the class. diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs index ae1995da47..3b3af1c73f 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs @@ -25,19 +25,16 @@ public class UsingRadioBatteryEventArgs : IPlayerEvent, IDeniableEvent, IItemEve /// /// /// - /// - /// - /// /// /// /// /// /// /// - public UsingRadioBatteryEventArgs(RadioItem radio, Player player, float drain, bool isAllowed = true) + public UsingRadioBatteryEventArgs(RadioItem radio, float drain, bool isAllowed = true) { Radio = Item.Get(radio); - Player = player; + Player = Radio.Owner; Drain = drain; IsAllowed = isAllowed; } diff --git a/EXILED/Exiled.Events/EventArgs/Scp049/ActivatingSenseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp049/ActivatingSenseEventArgs.cs index c8ae4fdae2..a6144bbb8c 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp049/ActivatingSenseEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp049/ActivatingSenseEventArgs.cs @@ -29,8 +29,8 @@ public ActivatingSenseEventArgs(Player player, Player target, bool isAllowed = t Scp049 = player.Role.As(); Target = target; IsAllowed = isAllowed; - FailedCooldown = (float)Scp049.SenseAbilityFailCooldown; - Duration = (float)Scp049.SenseAbilityDuration; + FailedCooldown = PlayerRoles.PlayableScps.Scp049.Scp049SenseAbility.AttemptFailCooldown; + Duration = PlayerRoles.PlayableScps.Scp049.Scp049SenseAbility.EffectDuration; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Scp049/SendingCallEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp049/SendingCallEventArgs.cs index 2d302846cf..e859023690 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp049/SendingCallEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp049/SendingCallEventArgs.cs @@ -25,7 +25,7 @@ public SendingCallEventArgs(Player player, bool isAllowed = true) { Player = player; Scp049 = Player.Role.As(); - Duration = (float)Scp049.CallAbilityBaseCooldown; + Duration = PlayerRoles.PlayableScps.Scp049.Scp049CallAbility.BaseCooldown; IsAllowed = isAllowed; } diff --git a/EXILED/Exiled.Events/EventArgs/Scp0492/ConsumingCorpseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp0492/ConsumingCorpseEventArgs.cs index 008ef83591..06de15a086 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp0492/ConsumingCorpseEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp0492/ConsumingCorpseEventArgs.cs @@ -25,10 +25,8 @@ public class ConsumingCorpseEventArgs : IScp0492Event, IRagdollEvent, IDeniableE /// /// /// - /// /// See for all ragdolls consumed. - // TODO: remove isAllowed argument - public ConsumingCorpseEventArgs(ReferenceHub player, BasicRagdoll ragDoll, ZombieConsumeAbility.ConsumeError error, bool isAllowed = true) + public ConsumingCorpseEventArgs(ReferenceHub player, BasicRagdoll ragDoll, ZombieConsumeAbility.ConsumeError error) { Player = Player.Get(player); Scp0492 = Player.Role.As(); diff --git a/EXILED/Exiled.Events/EventArgs/Scp1344/ChangedStatusEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1344/ChangedStatusEventArgs.cs index 11afddf79c..c5c772529f 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp1344/ChangedStatusEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp1344/ChangedStatusEventArgs.cs @@ -7,8 +7,12 @@ namespace Exiled.Events.EventArgs.Scp1344 { + using System; + using Exiled.API.Features.Items; using Exiled.Events.EventArgs.Interfaces; + + using InventorySystem.Items; using InventorySystem.Items.Usables.Scp1344; /// @@ -21,11 +25,10 @@ public class ChangedStatusEventArgs : IScp1344Event, IPlayerEvent, IDeniableEven /// /// /// - public ChangedStatusEventArgs(Item item, Scp1344Status scp1344Status) + public ChangedStatusEventArgs(ItemBase item, Scp1344Status scp1344Status) { - Item = item; - Scp1344 = item as Scp1344; - Player = item.Owner; + Scp1344 = Item.Get(item); + Player = Scp1344.Owner; Scp1344Status = scp1344Status; } @@ -37,7 +40,7 @@ public ChangedStatusEventArgs(Item item, Scp1344Status scp1344Status) /// /// Gets the item. /// - public Item Item { get; } + public Item Item => Scp1344; /// /// Gets the player in owner of the item. @@ -50,6 +53,7 @@ public ChangedStatusEventArgs(Item item, Scp1344Status scp1344Status) public Scp1344 Scp1344 { get; } /// + [Obsolete("Please use ChangingStatusEventArgs::IsAllowed instead of this", true)] public bool IsAllowed { get; set; } } } diff --git a/EXILED/Exiled.Events/EventArgs/Scp1344/ChangingStatusEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1344/ChangingStatusEventArgs.cs index 89329e0730..6cf7449e99 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp1344/ChangingStatusEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp1344/ChangingStatusEventArgs.cs @@ -9,6 +9,7 @@ namespace Exiled.Events.EventArgs.Scp1344 { using Exiled.API.Features.Items; using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items; using InventorySystem.Items.Usables.Scp1344; /// @@ -23,11 +24,10 @@ public class ChangingStatusEventArgs : IPlayerEvent, IScp1344Event, IDeniableEve /// /// /// - public ChangingStatusEventArgs(Item item, Scp1344Status scp1344StatusNew, Scp1344Status scp1344StatusOld, bool isAllowed = true) + public ChangingStatusEventArgs(ItemBase item, Scp1344Status scp1344StatusNew, Scp1344Status scp1344StatusOld, bool isAllowed = true) { - Item = item; - Scp1344 = item as Scp1344; - Player = item.Owner; + Scp1344 = Item.Get(item); + Player = Scp1344.Owner; Scp1344StatusNew = scp1344StatusNew; Scp1344StatusOld = scp1344StatusOld; IsAllowed = isAllowed; @@ -46,7 +46,7 @@ public ChangingStatusEventArgs(Item item, Scp1344Status scp1344StatusNew, Scp134 /// /// Gets the item. /// - public Item Item { get; } + public Item Item => Scp1344; /// /// Gets the player in owner of the item. diff --git a/EXILED/Exiled.Events/EventArgs/Scp173/PlacingTantrumEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp173/PlacingTantrumEventArgs.cs index 459befc3b5..70ed06bdf4 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp173/PlacingTantrumEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp173/PlacingTantrumEventArgs.cs @@ -7,6 +7,8 @@ namespace Exiled.Events.EventArgs.Scp173 { + using System; + using Exiled.API.Features; using Exiled.Events.EventArgs.Interfaces; @@ -39,7 +41,9 @@ public PlacingTantrumEventArgs(Player player, TantrumEnvironmentalHazard tantrum { Player = player; Scp173 = Player.Role.As(); +#pragma warning disable CS0618 TantrumHazard = tantrumHazard; +#pragma warning restore CS0618 Cooldown = cooldown; IsAllowed = isAllowed; } @@ -52,6 +56,7 @@ public PlacingTantrumEventArgs(Player player, TantrumEnvironmentalHazard tantrum /// /// Gets the . /// + [Obsolete("This propperty is always null")] public TantrumEnvironmentalHazard TantrumHazard { get; } /// diff --git a/EXILED/Exiled.Events/EventArgs/Scp173/UsingBreakneckSpeedsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp173/UsingBreakneckSpeedsEventArgs.cs index 8b380e7389..32216ba9e0 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp173/UsingBreakneckSpeedsEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp173/UsingBreakneckSpeedsEventArgs.cs @@ -22,13 +22,17 @@ public class UsingBreakneckSpeedsEventArgs : IScp173Event, IDeniableEvent /// /// /// + /// + /// + /// /// /// /// - public UsingBreakneckSpeedsEventArgs(Player player, bool isAllowed = true) + public UsingBreakneckSpeedsEventArgs(Player player, bool isActivationRequested, bool isAllowed = true) { Player = player; Scp173 = player.Role.As(); + IsActivationRequested = isActivationRequested; IsAllowed = isAllowed; } @@ -37,6 +41,11 @@ public UsingBreakneckSpeedsEventArgs(Player player, bool isAllowed = true) /// public bool IsAllowed { get; set; } + /// + /// Gets a value indicating whether the player is attempting to activate breakneck speeds. + /// + public bool IsActivationRequested { get; } + /// /// Gets the player who's using breakneck speeds. /// @@ -45,4 +54,4 @@ public UsingBreakneckSpeedsEventArgs(Player player, bool isAllowed = true) /// public Scp173Role Scp173 { get; } } -} \ No newline at end of file +} diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs index dc39d420c5..d706ea289d 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs @@ -21,31 +21,29 @@ public class InteractingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableE /// /// Initializes a new instance of the class. /// - /// + /// /// /// /// /// /// - public InteractingScp330EventArgs(Player player, int usage) + /// + /// + /// + /// + /// + /// + /// + /// + /// + public InteractingScp330EventArgs(ReferenceHub referenceHub, int usage, bool shouldPlaySound, bool shouldSever, CandyKindID candy) { - Player = player; + Player = Player.Get(referenceHub); UsageCount = usage; ShouldSever = usage >= 2; - ShouldPlaySound = true; + ShouldPlaySound = shouldPlaySound; IsAllowed = Player.IsHuman; Candy = Scp330Candies.GetRandom(); - - if (Scp330Bag.TryGetBag(player.ReferenceHub, out Scp330Bag scp330Bag)) - { - Scp330 = (Scp330)Item.Get(scp330Bag); - } - else - { - Scp330 = (Scp330)Item.Create(ItemType.SCP330, player); - Scp330.RemoveAllCandy(); - player.AddItem(Scp330); - } } /// diff --git a/EXILED/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs index 216676772b..2e5c11dc05 100644 --- a/EXILED/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Server/EndingRoundEventArgs.cs @@ -7,6 +7,8 @@ namespace Exiled.Events.EventArgs.Server { + using System; + using API.Enums; using Interfaces; @@ -24,17 +26,13 @@ public class EndingRoundEventArgs : IDeniableEvent /// /// /// - /// - /// - /// /// /// /// - public EndingRoundEventArgs(LeadingTeam leadingTeam, RoundSummary.SumInfo_ClassList classList, bool isForceEnded, bool isAllowed) + public EndingRoundEventArgs(LeadingTeam leadingTeam, RoundSummary.SumInfo_ClassList classList, bool isAllowed) { LeadingTeam = leadingTeam; ClassList = classList; - IsForceEnded = isForceEnded; IsAllowed = isAllowed; } @@ -51,7 +49,12 @@ public EndingRoundEventArgs(LeadingTeam leadingTeam, RoundSummary.SumInfo_ClassL /// /// Gets or sets a value indicating whether the round is ended by API call. /// - public bool IsForceEnded { get; set; } + [Obsolete("This event is now call only when it's haven't been force eneded")] + public bool IsForceEnded + { + get => false; // This event is now call only when ForceEnd method haven't been called + set => IsAllowed = value; + } /// /// Gets or sets a value indicating whether the round is going to finish or not. diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs index ac37f5528a..103ba58829 100644 --- a/EXILED/Exiled.Events/Events.cs +++ b/EXILED/Exiled.Events/Events.cs @@ -20,7 +20,7 @@ namespace Exiled.Events using InventorySystem.Items.Usables; using PlayerRoles.Ragdolls; using PlayerRoles.RoleAssign; - using PluginAPI.Events; + using Respawning; using UnityEngine.SceneManagement; using UserSettings.ServerSpecific; @@ -81,11 +81,13 @@ public override void OnEnabled() RagdollManager.OnRagdollRemoved += Handlers.Internal.RagdollList.OnRemovedRagdoll; ItemPickupBase.OnPickupAdded += Handlers.Internal.PickupEvent.OnSpawnedPickup; ItemPickupBase.OnPickupDestroyed += Handlers.Internal.PickupEvent.OnRemovedPickup; - ServerConsole.ReloadServerName(); + + AdminToys.AdminToyBase.OnAdded += Handlers.Internal.AdminToyList.OnAddedAdminToys; + AdminToys.AdminToyBase.OnRemoved += Handlers.Internal.AdminToyList.OnRemovedAdminToys; ServerSpecificSettingsSync.ServerOnSettingValueReceived += SettingBase.OnSettingUpdated; - EventManager.RegisterEvents(this); + ServerConsole.ReloadServerName(); } /// @@ -118,8 +120,6 @@ public override void OnDisabled() ItemPickupBase.OnPickupDestroyed -= Handlers.Internal.PickupEvent.OnRemovedPickup; ServerSpecificSettingsSync.ServerOnSettingValueReceived -= SettingBase.OnSettingUpdated; - - EventManager.UnregisterEvents(this); } /// diff --git a/EXILED/Exiled.Events/Exiled.Events.csproj b/EXILED/Exiled.Events/Exiled.Events.csproj index aa7f9ff4bf..620625057d 100644 --- a/EXILED/Exiled.Events/Exiled.Events.csproj +++ b/EXILED/Exiled.Events/Exiled.Events.csproj @@ -26,10 +26,10 @@ + - diff --git a/EXILED/Exiled.Events/Handlers/Internal/AdminToyList.cs b/EXILED/Exiled.Events/Handlers/Internal/AdminToyList.cs new file mode 100644 index 0000000000..2d4ebf971e --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Internal/AdminToyList.cs @@ -0,0 +1,27 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers.Internal +{ + /// + /// Handles adding and removing from . + /// + internal static class AdminToyList + { + /// + /// Called after a ragdoll is spawned. Hooked to . + /// + /// The spawned ragdoll. + public static void OnAddedAdminToys(AdminToys.AdminToyBase adminToy) => API.Features.Toys.AdminToy.Get(adminToy); + + /// + /// Called before a ragdoll is destroyed. Hooked to . + /// + /// The destroyed ragdoll. + public static void OnRemovedAdminToys(AdminToys.AdminToyBase adminToy) => API.Features.Toys.AdminToy.BaseToAdminToy.Remove(adminToy); + } +} diff --git a/EXILED/Exiled.Events/Handlers/Internal/ClientStarted.cs b/EXILED/Exiled.Events/Handlers/Internal/ClientStarted.cs index a260cf4957..8115dc4e65 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/ClientStarted.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/ClientStarted.cs @@ -33,21 +33,38 @@ public static void OnClientStarted() foreach (KeyValuePair prefab in NetworkClient.prefabs) { - if(!prefabs.ContainsKey(prefab.Key)) + if (!prefabs.ContainsKey(prefab.Key)) prefabs.Add(prefab.Key, prefab.Value); } foreach (NetworkIdentity ragdollPrefab in RagdollManager.AllRagdollPrefabs) { - if(!prefabs.ContainsKey(ragdollPrefab.assetId)) + if (!prefabs.ContainsKey(ragdollPrefab.assetId)) prefabs.Add(ragdollPrefab.assetId, ragdollPrefab.gameObject); } - foreach (PrefabType prefabType in EnumUtils.Values) + for (int i = 0; i < EnumUtils.Values.Length; i++) { + PrefabType prefabType = EnumUtils.Values[i]; PrefabAttribute attribute = prefabType.GetPrefabAttribute(); - PrefabHelper.Prefabs.Add(prefabType, prefabs.FirstOrDefault(prefab => prefab.Key == attribute.AssetId || prefab.Value.name.Contains(attribute.Name)).Value); + if (prefabs.TryGetValue(attribute.AssetId, out GameObject gameObject)) + { + PrefabHelper.Prefabs.Add(prefabType, gameObject); + prefabs.Remove(attribute.AssetId); + continue; + } + + KeyValuePair? value = prefabs.FirstOrDefault(x => x.Value.name == attribute.Name); + if (value.HasValue) + { + PrefabHelper.Prefabs.Add(prefabType, gameObject); + prefabs.Remove(value.Value.Key); + continue; + } } + + foreach (KeyValuePair missing in prefabs) + Log.Warn($"Missing prefab in {nameof(PrefabType)}: {missing.Value.name} ({missing.Key})"); } } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Internal/Round.cs b/EXILED/Exiled.Events/Handlers/Internal/Round.cs index 2091870dfa..99212af062 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/Round.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/Round.cs @@ -62,7 +62,6 @@ public static void OnRestartingRound() Scp173Role.TurnedPlayers.Clear(); Scp096Role.TurnedPlayers.Clear(); Scp079Role.TurnedPlayers.Clear(); - Scp939Role.TurnedPlayers.Clear(); MultiAdminFeatures.CallEvent(MultiAdminFeatures.EventType.ROUND_END); diff --git a/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs b/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs index 63bb70267e..55b9d0738b 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs @@ -35,7 +35,6 @@ public static void OnSceneUnloaded(Scene _) { Player.UserIdsCache.Clear(); Player.Dictionary.Clear(); - AdminToy.BaseToAdminToy.Clear(); } } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Item.cs b/EXILED/Exiled.Events/Handlers/Item.cs index 1c3f8b15e0..d8153a586e 100644 --- a/EXILED/Exiled.Events/Handlers/Item.cs +++ b/EXILED/Exiled.Events/Handlers/Item.cs @@ -54,10 +54,23 @@ public static class Item public static Event UsingRadioPickupBattery { get; set; } = new(); /// - /// Invoked before a state is changed. + /// Invoked before a state is changed. /// public static Event ChangingMicroHIDPickupState { get; set; } = new(); + /// + /// Invoked before a firing while on the ground. + /// The client will still see all effects, like sounds and shoot. + /// + public static Event DisruptorFiring { get; set; } = new(); + + /// + /// Called before a firing while on the ground. + /// WARNING: Client still receive the shoot sound AND the ammo is still removed. (even if = false). + /// + /// The instance. + public static void OnDisruptorFiring(DisruptorFiringEventArgs ev) => DisruptorFiring.InvokeSafely(ev); + /// /// Called before the ammo of an firearm is changed. /// diff --git a/EXILED/Exiled.Events/Handlers/Map.cs b/EXILED/Exiled.Events/Handlers/Map.cs index 00d73e9ba0..4db303b36d 100644 --- a/EXILED/Exiled.Events/Handlers/Map.cs +++ b/EXILED/Exiled.Events/Handlers/Map.cs @@ -105,6 +105,11 @@ public static class Map /// public static Event PickupDestroyed { get; set; } = new(); + /// + /// Invoked before a room connector spawns. + /// + public static Event SpawningRoomConnector { get; set; } = new(); + /// /// Invoked before a team vehicle is spawned. /// @@ -221,6 +226,12 @@ public static class Map /// The instance. public static void OnPickupDestroyed(PickupDestroyedEventArgs ev) => PickupDestroyed.InvokeSafely(ev); + /// + /// Invoked before a room connector spawns. + /// + /// The instance. + public static void OnSpawningRoomConnector(SpawningRoomConnectorEventArgs ev) => SpawningRoomConnector.InvokeSafely(ev); + /// /// Invoked before a team vehicle is spawned. /// diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index ac58bb3085..e35df7b044 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -17,15 +17,16 @@ namespace Exiled.Events.Handlers using Exiled.Events.Features; - using PluginAPI.Core.Attributes; - using PluginAPI.Enums; - using PluginAPI.Events; - /// /// Player related events. /// public class Player { + /// + /// Invoked after a player triggers the attack as an SCP. + /// + public static Event Hit { get; set; } = new (); + /// /// Invoked before authenticating a . /// @@ -583,6 +584,16 @@ public class Player /// public static Event ChangingDisruptorMode { get; set; } = new(); + /// + /// Invoked before the player explode with the micro HID. + /// + public static Event ExplodingMicroHID { get; set; } = new(); + + /// + /// Invoked before the micro HID opens a door. + /// + public static Event MicroHIDOpeningDoor { get; set; } = new(); + /// /// Invoked before player interacts with coffee cup. /// @@ -1260,6 +1271,18 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// The instance. public static void OnChangingDisruptorMode(ChangingDisruptorModeEventArgs ev) => ChangingDisruptorMode.InvokeSafely(ev); + /// + /// Called before disruptor's mode is changed. + /// + /// The instance. + public static void OnExplodingMicroHID(ExplodingMicroHIDEventArgs ev) => ExplodingMicroHID.InvokeSafely(ev); + + /// + /// Called before the micro HID opens a door. + /// + /// The instance. + public static void OnMicroHIDOpeningDoor(MicroHIDOpeningDoorEventArgs ev) => MicroHIDOpeningDoor.InvokeSafely(ev); + /// /// Called before player interacts with coffee cup. /// @@ -1272,5 +1295,11 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// /// instance. public static void OnPreAuthenticating(PreAuthenticatingEventArgs ev) => PreAuthenticating.InvokeSafely(ev); + + /// + /// Called after a player triggers the melee attack as an SCP. + /// + /// The instance. + public static void OnHit(HitEventArgs ev) => Hit.InvokeSafely(ev); } } diff --git a/EXILED/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs b/EXILED/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs index 1bdacfd8f2..0cb378ac02 100644 --- a/EXILED/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs +++ b/EXILED/Exiled.Events/Patches/Events/Cassie/SendingCassieMessage.cs @@ -22,7 +22,7 @@ namespace Exiled.Events.Patches.Events.Cassie using static HarmonyLib.AccessTools; /// - /// Patches . + /// Patches . /// Adds the event. /// [EventPatch(typeof(Cassie), nameof(Cassie.SendingCassieMessage))] @@ -35,35 +35,62 @@ private static IEnumerable Transpiler(IEnumerable +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Item +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pickups; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Item; + using Footprinting; + using HarmonyLib; + using InventorySystem.Items; + using InventorySystem.Items.Firearms; + using InventorySystem.Items.Firearms.Extensions; + using InventorySystem.Items.Firearms.Modules; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . Adds the event. + /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.DisruptorFiring))] + [HarmonyPatch(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension.ServerFire))] + public class DisruptorFiring + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + LocalBuilder ev = generator.DeclareLocal(typeof(DisruptorFiringEventArgs)); + Label continueLabel = generator.DefineLabel(); + newInstructions.InsertRange(0, new CodeInstruction[] + { + // Pickup.Get(this._worldmodel.Identifier.SerialNumber) + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._worldmodel))), + new(OpCodes.Callvirt, PropertyGetter(typeof(FirearmWorldmodel), nameof(FirearmWorldmodel.Identifier))), + new(OpCodes.Ldfld, Field(typeof(ItemIdentifier), nameof(ItemIdentifier.SerialNumber))), + new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ushort) })), + + // Player.Get(this._scheduledAttackerFootprint) + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._scheduledAttackerFootprint))), + new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(Footprint) })), + + // this._scheduledFiringState + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._scheduledFiringState))), + + // true + new(OpCodes.Ldc_I4_0), + + // DisruptorFiringEventArgs ev = new DisruptorFiringEventArgs(Pickup.Get(this._worldmodel.Identifier.SerialNumber), Player.Get(this._scheduledAttackerFootprint), this._scheduledFiringState, true) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DisruptorFiringEventArgs))[0]), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Item.OnDisruptorFiring(ev); + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnDisruptorFiring))), + + // if (!ev.IsAllowed) return; + new(OpCodes.Ldloc_S, ev.LocalIndex), + new (OpCodes.Callvirt, PropertyGetter(typeof(DisruptorFiringEventArgs), nameof(DisruptorFiringEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + new(OpCodes.Ret), + + // this._scheduledAttackerFootprint = Attacker.Footprint; + new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).WithLabels(continueLabel), + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(DisruptorFiringEventArgs), nameof(DisruptorFiringEventArgs.Attacker))), + new(OpCodes.Callvirt, PropertyGetter(typeof(API.Features.Player), nameof(API.Features.Player.Footprint))), + new(OpCodes.Stfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._scheduledAttackerFootprint))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs b/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs index bf3e54472b..62faa353cd 100644 --- a/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs +++ b/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs @@ -13,8 +13,7 @@ namespace Exiled.Events.Patches.Events.Item using API.Features; using API.Features.Pickups; using API.Features.Pools; - - using Exiled.Events; + using Attributes; using Exiled.Events.EventArgs.Item; using Footprinting; @@ -22,9 +21,6 @@ namespace Exiled.Events.Patches.Events.Item using HarmonyLib; using Interactables.Interobjects.DoorUtils; - - using InventorySystem.Items; - using UnityEngine; using static HarmonyLib.AccessTools; @@ -35,6 +31,7 @@ namespace Exiled.Events.Patches.Events.Item /// Patches and adds implementation. /// Adds the event. /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.KeycardInteracting))] [HarmonyPatch(typeof(BaseKeycardPickup), nameof(BaseKeycardPickup.ProcessCollision))] internal static class KeycardInteracting { @@ -43,113 +40,65 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); LocalBuilder isUnlocked = generator.DeclareLocal(typeof(bool)); - LocalBuilder notEmptyPermissions = generator.DeclareLocal(typeof(bool)); - LocalBuilder havePermissions = generator.DeclareLocal(typeof(bool)); + LocalBuilder hasPermission = generator.DeclareLocal(typeof(bool)); - Label skip = generator.DefineLabel(); Label ret = generator.DefineLabel(); int offset = 1; int index = newInstructions.FindIndex(i => i.LoadsField(Field(typeof(DoorVariant), nameof(DoorVariant.ActiveLocks)))) + offset; + Label continueLabel = (Label)newInstructions[index].operand; + newInstructions.RemoveAt(index); newInstructions.InsertRange( index, - new[] + new CodeInstruction[] { // check and write door lock state (isUnlocked) new(OpCodes.Ldc_I4_0), new(OpCodes.Ceq), - new CodeInstruction(OpCodes.Stloc_S, isUnlocked.LocalIndex), + new(OpCodes.Stloc_S, isUnlocked.LocalIndex), + new(OpCodes.Br, continueLabel), }); - index = newInstructions.FindIndex(i => i.LoadsField(Field(typeof(DoorPermissions), nameof(DoorPermissions.RequiredPermissions)))) + offset; + offset = 1; + index = newInstructions.FindIndex(i => i.Calls(Method( + typeof(DoorPermissionsPolicyExtensions), + nameof(DoorPermissionsPolicyExtensions.CheckPermissions), + new[] { typeof(IDoorPermissionRequester), typeof(IDoorPermissionProvider), typeof(PermissionUsed).MakeByRefType() }))) + offset; newInstructions.InsertRange( index, - new[] - { - // checking empty permissions - new(OpCodes.Ldc_I4_0), - new(OpCodes.Cgt), - - new(OpCodes.Stloc_S, notEmptyPermissions.LocalIndex), - new(OpCodes.Br_S, skip), - - // save original return - new CodeInstruction(OpCodes.Ret).MoveLabelsFrom(newInstructions[index + 1]), - new CodeInstruction(OpCodes.Nop).WithLabels(skip), - }); - - // 6 new instructions - offset = 6; - index += offset; + new CodeInstruction[] + { + // hasPermission + new(OpCodes.Stloc_S, hasPermission.LocalIndex), - newInstructions.RemoveRange(index, 14); + // pickup + new(OpCodes.Ldarg_0), - newInstructions.InsertRange( - index, - new[] - { - // override permissions check, to implement KeycardPickup::Permissions - new(OpCodes.Ldarg_0), - new(OpCodes.Ldloc_1), - new CodeInstruction(OpCodes.Call, Method(typeof(KeycardInteracting), nameof(KeycardInteracting.CheckPermissions))), - new CodeInstruction(OpCodes.Stloc_S, havePermissions.LocalIndex), - }); + // PreviousOwner.Hub + new(OpCodes.Ldarg_0), + new(OpCodes.Ldflda, Field(typeof(BaseKeycardPickup), nameof(BaseKeycardPickup.PreviousOwner))), + new(OpCodes.Ldfld, Field(typeof(Footprint), nameof(Footprint.Hub))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), - // 4 new instructions - offset = 4; - index += offset; + // door + new(OpCodes.Ldloc_1), - newInstructions.RemoveRange(index, 2); + // isAllowed = isUnlocked && hasPermission + new(OpCodes.Ldloc_S, isUnlocked.LocalIndex), + new(OpCodes.Ldloc_S, hasPermission.LocalIndex), + new(OpCodes.And), - offset = -5; - index = newInstructions.FindIndex(i => i.Calls(PropertySetter(typeof(DoorVariant), nameof(DoorVariant.NetworkTargetState)))) + offset; + // ev = new KeycardInteractingEventArgs(pickup, player, door, isAllowed) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(KeycardInteractingEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnKeycardInteracting))), - newInstructions.InsertRange( - index, - new[] - { - // pickup - new(OpCodes.Ldarg_0), - - // PreviousOwner.Hub - new CodeInstruction(OpCodes.Ldarg_0), - new(OpCodes.Ldflda, Field(typeof(BaseKeycardPickup), nameof(BaseKeycardPickup.PreviousOwner))), - new(OpCodes.Ldfld, Field(typeof(Footprint), nameof(Footprint.Hub))), - new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), - - // door - new(OpCodes.Ldloc_1), - - // allowed calculate - new(OpCodes.Ldloc_S, isUnlocked), - - new(OpCodes.Ldloc_S, havePermissions), - - new(OpCodes.Ldloc_S, notEmptyPermissions), - new(OpCodes.Call, PropertyGetter(typeof(Events), nameof(Events.Instance))), - new(OpCodes.Callvirt, PropertyGetter(typeof(Events), nameof(Events.Config))), - new(OpCodes.Callvirt, PropertyGetter(typeof(Config), nameof(Config.CanKeycardThrowAffectDoors))), - new(OpCodes.Or), - - new(OpCodes.And), - new(OpCodes.And), - - // ThrowKeycardInteractingEventArgs ev = new(pickup, player, door, isAllowed); - // - // Item.OnThrowKeycardInteracting(ev); - // - // if (!ev.IsAllowed) - // return; - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(KeycardInteractingEventArgs))[0]), - new(OpCodes.Dup), - new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnKeycardInteracting))), - new(OpCodes.Callvirt, PropertyGetter(typeof(KeycardInteractingEventArgs), nameof(KeycardInteractingEventArgs.IsAllowed))), - new(OpCodes.Brfalse_S, ret), - }); + new(OpCodes.Callvirt, PropertyGetter(typeof(KeycardInteractingEventArgs), nameof(KeycardInteractingEventArgs.IsAllowed))), + }); newInstructions.InsertRange( newInstructions.Count - 1, @@ -167,26 +116,5 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } - - private static bool CheckPermissions(BaseKeycardPickup keycard, DoorVariant door) - { - DoorPermissions permissions = door.RequiredPermissions; - if (permissions.RequiredPermissions == KeycardPermissions.None) - { - return true; - } - - if (Pickup.Get(keycard) is KeycardPickup keycardPickup) - { - if (!permissions.RequireAll) - { - return ((KeycardPermissions)keycardPickup.Permissions & permissions.RequiredPermissions) != 0; - } - - return ((KeycardPermissions)keycardPickup.Permissions & permissions.RequiredPermissions) == permissions.RequiredPermissions; - } - - return InventorySystem.InventoryItemLoader.AvailableItems.TryGetValue(keycard.Info.ItemId, out ItemBase itemBase) && permissions.CheckPermissions(itemBase, null); - } } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingChaosEntrance.cs b/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingChaosEntrance.cs index 1bf2c9c02d..0d53ebeefb 100644 --- a/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingChaosEntrance.cs +++ b/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingChaosEntrance.cs @@ -42,7 +42,7 @@ private static IEnumerable Transpiler(IEnumerable i.opcode == OpCodes.Ldloca_S); + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Pop) + 1; newInstructions.InsertRange(index, new[] { diff --git a/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs b/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs index f9663ecc7e..4d169bf050 100644 --- a/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs +++ b/EXILED/Exiled.Events/Patches/Events/Map/AnnouncingNtfEntrance.cs @@ -55,8 +55,8 @@ private static IEnumerable Transpiler(IEnumerable x.characterClassManager.CurRole.team == Team.SCP && x.characterClassManager.CurClass != RoleTypeId.Scp0492); - new(OpCodes.Ldloc_3), + // scpsLeft + new(OpCodes.Ldloc_2), // string[] unitInformation = unitNameClear.Split('-'); new(OpCodes.Ldloc_1), @@ -111,7 +111,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable i.opcode == OpCodes.Ldloc_0) + offset; + int offset = -3; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Newobj && (ConstructorInfo)i.operand == GetDeclaredConstructors(typeof(LabApi.Events.Arguments.ServerEvents.CassieQueuingScpTerminationEventArgs))[0]) + offset; newInstructions.InsertRange( index, @@ -53,10 +54,7 @@ private static IEnumerable Transpiler(IEnumerable targetToAffect = HashSetPool.Pool.Get(); foreach (Player player in ReferenceHub.AllHubs.Select(Player.Get)) { - if ((instance.transform.position - player.Position).sqrMagnitude >= distance) + if ((instance.transform.position - player.Position).sqrMagnitude > distance) continue; + if (!ExiledEvents.Instance.Config.CanFlashbangsAffectThrower && instance.PreviousOwner.CompareLife(player.ReferenceHub)) continue; + if (!IndividualFriendlyFire.CheckFriendlyFirePlayer(instance.PreviousOwner, player.ReferenceHub) && !instance.PreviousOwner.CompareLife(player.ReferenceHub)) continue; - if (Physics.Linecast(instance.transform.position, player.CameraTransform.position, instance._blindingMask)) + + if (Physics.Linecast(instance.transform.position, player.CameraTransform.position, instance.BlindingMask)) continue; targetToAffect.Add(player); diff --git a/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs b/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs index e29c8c621e..ad35198c58 100644 --- a/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs +++ b/EXILED/Exiled.Events/Patches/Events/Map/ExplodingFragGrenade.cs @@ -62,53 +62,62 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); int offset = 1; - int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Stloc_3) + offset; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Stloc_S && i.operand is LocalBuilder { LocalIndex: 5 }) + offset; - Label returnLabel = generator.DefineLabel(); + Label continueLabel = generator.DefineLabel(); LocalBuilder ev = generator.DeclareLocal(typeof(ExplodingGrenadeEventArgs)); - newInstructions.InsertRange( - index, - new CodeInstruction[] - { - // attacker; - new(OpCodes.Ldarg_0), - - // position - new(OpCodes.Ldarg_1), - - // grenade - new(OpCodes.Ldarg_2), - - // Collider[] - new(OpCodes.Ldloc_3), - - // explosionType - new(OpCodes.Ldarg_3), - - // ExplodingGrenadeEventArgs ev = new(player, position, grenade, colliders, ExplosionType); - new(OpCodes.Newobj, DeclaredConstructor(typeof(ExplodingGrenadeEventArgs), new[] { typeof(Footprint), typeof(Vector3), typeof(ExplosionGrenade), typeof(Collider[]), typeof(ExplosionType) })), - new(OpCodes.Dup), - new(OpCodes.Dup), - new(OpCodes.Stloc, ev.LocalIndex), - - // Map.OnExplodingGrenade(ev); - new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnExplodingGrenade))), - - // if (!ev.IsAllowed) - // return; - new(OpCodes.Callvirt, PropertyGetter(typeof(ExplodingGrenadeEventArgs), nameof(ExplodingGrenadeEventArgs.IsAllowed))), - new(OpCodes.Brfalse, returnLabel), - - // colliders = TrimColliders(ev, colliders) - new(OpCodes.Ldloc, ev.LocalIndex), - new(OpCodes.Ldloc_3), - new(OpCodes.Call, Method(typeof(ExplodingFragGrenade), nameof(TrimColliders))), - new(OpCodes.Stloc_3), - }); - - newInstructions[newInstructions.Count - 1].labels.Add(returnLabel); + newInstructions.InsertRange(index, new CodeInstruction[] + { + // attacker; + new(OpCodes.Ldarg_0), + + // position + new(OpCodes.Ldarg_1), + + // grenade + new(OpCodes.Ldarg_2), + + // Collider[] + new(OpCodes.Ldloc_S, 5), + + // explosionType + new(OpCodes.Ldarg_3), + + // ExplodingGrenadeEventArgs ev = new(Footprint, position, grenade, colliders, ExplosionType); + new(OpCodes.Newobj, DeclaredConstructor(typeof(ExplodingGrenadeEventArgs), new[] { typeof(Footprint), typeof(Vector3), typeof(ExplosionGrenade), typeof(Collider[]), typeof(ExplosionType) })), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc, ev.LocalIndex), + + // Map.OnExplodingGrenade(ev); + new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnExplodingGrenade))), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(ExplodingGrenadeEventArgs), nameof(ExplodingGrenadeEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, continueLabel), + + // HashSetPool.Shared.Return(hashSet); + new(OpCodes.Ldsfld, Field(typeof(NorthwoodLib.Pools.HashSetPool), nameof(NorthwoodLib.Pools.HashSetPool.Shared))), + new(OpCodes.Ldloc_2), + new(OpCodes.Callvirt, Method(typeof(NorthwoodLib.Pools.HashSetPool), nameof(NorthwoodLib.Pools.HashSetPool.Return))), + + // HashSetPool.Shared.Return(hashSet2); + new(OpCodes.Ldsfld, Field(typeof(NorthwoodLib.Pools.HashSetPool), nameof(NorthwoodLib.Pools.HashSetPool.Shared))), + new(OpCodes.Ldloc_3), + new(OpCodes.Callvirt, Method(typeof(NorthwoodLib.Pools.HashSetPool), nameof(NorthwoodLib.Pools.HashSetPool.Return))), + + // return; + new(OpCodes.Ret), + + // colliders = TrimColliders(ev, colliders) + new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex).WithLabels(continueLabel), + new(OpCodes.Ldloc_S, 5), + new(OpCodes.Call, Method(typeof(ExplodingFragGrenade), nameof(TrimColliders))), + new(OpCodes.Stloc_S, 5), + }); for (int z = 0; z < newInstructions.Count; z++) yield return newInstructions[z]; diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs index 0293950826..df768a3048 100644 --- a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs +++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs @@ -24,11 +24,11 @@ namespace Exiled.Events.Patches.Events.Map using static HarmonyLib.AccessTools; /// - /// Patches . + /// Patches . /// Adds the event. /// [EventPatch(typeof(Map), nameof(Map.SpawningItem))] - [HarmonyPatch(typeof(ItemDistributor), nameof(ItemDistributor.CreatePickup))] + [HarmonyPatch(typeof(ItemDistributor), nameof(ItemDistributor.ServerRegisterPickup))] internal static class SpawningItem { private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs new file mode 100644 index 0000000000..b379bdec1a --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs @@ -0,0 +1,106 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Map +{ + using MapGeneration.RoomConnectors; + +#pragma warning disable SA1402 // File may only contain a single type + + using System.Collections.Generic; + using System.Reflection; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Map; + using HarmonyLib; + using MapGeneration; + using MapGeneration.RoomConnectors.Spawners; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.SpawningRoomConnector))] + [HarmonyPatch(typeof(RoomConnectorSpawnpointBase), nameof(RoomConnectorSpawnpointBase.Spawn))] + internal static class SpawningRoomConnector + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + Label retLabel = generator.DefineLabel(); + + newInstructions.InsertRange(0, new CodeInstruction[] + { + // this + new(OpCodes.Ldarg_0), + + // type + new(OpCodes.Ldarg_1), + + // SpawningRoomConnectorEventArgs ev = new SpawningRoomConnectorEventArgs(RoomConnectorSpawnpointBase, SpawnableRoomConnectorType) + new(OpCodes.Newobj, Constructor( + typeof(SpawningRoomConnectorEventArgs), + new[] { typeof(RoomConnectorSpawnpointBase), typeof(SpawnableRoomConnectorType) })), + new(OpCodes.Dup), + new(OpCodes.Dup), + + // Handlers.Map.OnSpawningRoomConnector(ev); + new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnSpawningRoomConnector))), + + // type = ev.ConnectorType + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningRoomConnectorEventArgs), nameof(SpawningRoomConnectorEventArgs.ConnectorType))), + new(OpCodes.Starg_S, 1), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningRoomConnectorEventArgs), nameof(SpawningRoomConnectorEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + int earlyRetIndex = newInstructions.FindIndex(i => i.opcode == OpCodes.Ret); + newInstructions[earlyRetIndex].WithLabels(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.SpawningRoomConnector))] + [HarmonyPatch(typeof(SeedSynchronizer), nameof(SeedSynchronizer.GenerateLevel))] + internal static class SpawningRoomConnectorFix + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(RoomConnectorSpawnpointBase), nameof(RoomConnectorSpawnpointBase.SetupAllRoomConnectors)))); + List - [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.InteractingDoor))] + [EventPatch(typeof(Handlers.Player), nameof(Player.InteractingDoor))] [HarmonyPatch(typeof(DoorVariant), nameof(DoorVariant.ServerInteract), typeof(ReferenceHub), typeof(byte))] internal static class InteractingDoor { @@ -33,92 +33,166 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - LocalBuilder ev = generator.DeclareLocal(typeof(InteractingDoorEventArgs)); + LocalBuilder labEvent = generator.DeclareLocal(typeof(PlayerInteractedDoorEventArgs)); + + Label exiledEvContinue = generator.DefineLabel(); + Label labEvContinue = generator.DefineLabel(); + + int offset = -3; + int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(DoorVariant), nameof(DoorVariant.AllowInteracting)))) + offset; - List