diff --git a/EXILED/Exiled.API/Features/Items/Firearm.cs b/EXILED/Exiled.API/Features/Items/Firearm.cs index eeee66adcd..46d544eed7 100644 --- a/EXILED/Exiled.API/Features/Items/Firearm.cs +++ b/EXILED/Exiled.API/Features/Items/Firearm.cs @@ -21,21 +21,15 @@ namespace Exiled.API.Features.Items using Exiled.API.Interfaces; using Exiled.API.Structs; using Extensions; - using InventorySystem; - using InventorySystem.Items; using InventorySystem.Items.Autosync; - using InventorySystem.Items.Firearms; using InventorySystem.Items.Firearms.Attachments; using InventorySystem.Items.Firearms.Attachments.Components; - using InventorySystem.Items.Firearms.BasicMessages; using InventorySystem.Items.Firearms.Modules; - using InventorySystem.Items.Pickups; - using MEC; - using UnityEngine; + + using static InventorySystem.Items.Firearms.Modules.AnimatorReloaderModuleBase; using BaseFirearm = InventorySystem.Items.Firearms.Firearm; using FirearmPickup = Pickups.FirearmPickup; - using Object = UnityEngine.Object; /// /// A wrapper class for . @@ -63,20 +57,26 @@ public Firearm(BaseFirearm itemBase) foreach (ModuleBase module in Base.Modules) { - if (module is IPrimaryAmmoContainerModule primaryAmmoModule) + switch (module) { - PrimaryMagazine ??= (PrimaryMagazine)Magazine.Get(primaryAmmoModule); - continue; - } + case IPrimaryAmmoContainerModule primaryAmmoModule: + PrimaryMagazine ??= (PrimaryMagazine)Magazine.Get(primaryAmmoModule); + break; - if (module is IAmmoContainerModule ammoModule) - { - BarrelMagazine ??= (BarrelMagazine)Magazine.Get(ammoModule); - } + case IAmmoContainerModule ammoModule: + BarrelMagazine ??= (BarrelMagazine)Magazine.Get(ammoModule); + break; - if (module is HitscanHitregModuleBase hitregModule) - { - HitscanHitregModule = hitregModule; + case HitscanHitregModuleBase hitregModule: + HitscanHitregModule = hitregModule; + break; + + case AnimatorReloaderModuleBase animatorReloaderModule: + AnimatorReloaderModule = animatorReloaderModule; + break; + + default: + break; } } } @@ -146,6 +146,11 @@ public static IReadOnlyDictionary public HitscanHitregModuleBase HitscanHitregModule { get; } + /// + /// Gets an animator reloader module for the current firearm. + /// + public AnimatorReloaderModuleBase AnimatorReloaderModule { get; } + /// /// Gets or sets the amount of ammo in the firearm magazine. /// @@ -713,12 +718,52 @@ public void ClearPreferences() /// /// For specific reloading logic you also can use for avaible weapons. /// - public void Reload() + public void ForceReload() { - if (Base.TryGetModule(out AnimatorReloaderModuleBase module)) - { - module.StartReloading(); - } + if (AnimatorReloaderModule == null) + return; + + AnimatorReloaderModule.IsReloading = true; + AnimatorReloaderModule.SendRpcHeaderWithRandomByte(ReloaderMessageHeader.Reload); + } + + /// + /// Attempts to reload the firearm with server-side validation. + /// + /// if the firearm was successfully reloaded. Otherwise, . + public bool Reload() + { + if (AnimatorReloaderModule == null) + return false; + + return AnimatorReloaderModule.ServerTryReload(); + } + + /// + /// Attempts to unload the firearm with server-side validation. + /// + /// if the firearm was successfully unload. Otherwise, . + public bool Unload() + { + if (AnimatorReloaderModule == null) + return false; + + return AnimatorReloaderModule.ServerTryUnload(); + } + + /// + /// Forces the firearm's client-side unload animation, bypassing server-side checks. + /// + /// + /// This only plays the animation and is not guaranteed to result in a successful unload. For server-validated unloading, use . + /// + public void ForceUnload() + { + if (AnimatorReloaderModule == null) + return; + + AnimatorReloaderModule.IsUnloading = true; + AnimatorReloaderModule.SendRpcHeaderWithRandomByte(ReloaderMessageHeader.Unload); } /// diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 0f189f1e28..09d591e594 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -64,6 +64,7 @@ namespace Exiled.API.Features using VoiceChat.Playbacks; using static DamageHandlers.DamageHandlerBase; + using static InventorySystem.Items.Firearms.Modules.AnimatorReloaderModuleBase; using DamageHandlerBase = PlayerStatsSystem.DamageHandlerBase; using Firearm = Items.Firearm; @@ -1803,23 +1804,70 @@ public void TrySetCustomRoleFriendlyFire(string roleTypeId, Dictionary Whether the item was able to be added. public bool TryRemoveCustomeRoleFriendlyFire(string role) => CustomRoleFriendlyFireMultiplier.Remove(role); + /// + /// Forces the player's client to play the weapon reload animation, bypassing server-side checks. + /// + /// if the command to start reloading was sent. Otherwise, . + /// + /// This method does not check if the weapon can actually be reloaded. It only forces the animation and is not guaranteed to result in a successful reload. + /// + public bool ForceReloadWeapon() + { + if (CurrentItem is not Firearm firearm || firearm.AnimatorReloaderModule == null) + { + return false; + } + + firearm.AnimatorReloaderModule.IsReloading = true; + firearm.AnimatorReloaderModule.SendRpcHeaderWithRandomByte(ReloaderMessageHeader.Reload); + return true; + } + /// /// Forces the player to reload their current weapon. /// - /// if firearm was successfully reloaded. Otherwise, . + /// if the firearm was successfully reloaded. Otherwise, . public bool ReloadWeapon() { - if (CurrentItem is Firearm firearm) + if (CurrentItem is not Firearm firearm || firearm.AnimatorReloaderModule == null) { - // TODO not finish - /* - bool result = firearm.Base.Ammo.ServerTryReload(); - Connection.Send(new RequestMessage(firearm.Serial, RequestType.Reload)); - return result; - */ + return false; } - return false; + return firearm.AnimatorReloaderModule.ServerTryReload(); + } + + /// + /// Forces the player's client to play the weapon unload animation, bypassing server-side checks. + /// + /// if the command to start unloading was sent. Otherwise, . + /// + /// This method does not check if the weapon can actually be unloaded. It only forces the animation and is not guaranteed to result in a successful unload. + /// + public bool ForceUnloadWeapon() + { + if (CurrentItem is not Firearm firearm || firearm.AnimatorReloaderModule == null) + { + return false; + } + + firearm.AnimatorReloaderModule.IsUnloading = true; + firearm.AnimatorReloaderModule.SendRpcHeaderWithRandomByte(ReloaderMessageHeader.Unload); + return true; + } + + /// + /// Forces the player to unload their current weapon. + /// + /// if the firearm was successfully unloaded. Otherwise, . + public bool UnloadWeapon() + { + if (CurrentItem is not Firearm firearm || firearm.AnimatorReloaderModule == null) + { + return false; + } + + return firearm.AnimatorReloaderModule.ServerTryUnload(); } ///