Skip to content

Commit

Permalink
feat: Fix Reload & Unload & ClipSize & Added Reloaded & UnLoaded (#398)
Browse files Browse the repository at this point in the history
* Fix Unloading and Reloading not working when denied

* Fix Crash

* better doc

* Added UnloadedWeapon & ReloadedWeapon

* Fix CustomWeapon

* Wrong event moment

* missing negative

* Update CustomWeapon.cs

* HitscanHitregModuleBase (require testing)

* Fixing for AutomaticWeapon

* Warning

* Fix Unloading event getting call with reloading

* Update ReloadingWeaponEventArgs.cs

* Make than ClipSize 0 or Negative do not affect the CustomWeapon
  • Loading branch information
louis1706 authored Jan 19, 2025
1 parent a898dbf commit fa82597
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 52 deletions.
61 changes: 61 additions & 0 deletions EXILED/Exiled.API/Features/Items/Firearm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public Firearm(BaseFirearm itemBase)
{
BarrelMagazine ??= (BarrelMagazine)Magazine.Get(ammoModule);
}

if (module is HitscanHitregModuleBase hitregModule)
{
HitscanHitregModule = hitregModule;
}
}
}

Expand Down Expand Up @@ -136,6 +141,11 @@ public static IReadOnlyDictionary<Player, Dictionary<FirearmType, AttachmentIden
/// </remarks>
public BarrelMagazine BarrelMagazine { get; }

/// <summary>
/// Gets a primaty magazine for current firearm.
/// </summary>
public HitscanHitregModuleBase HitscanHitregModule { get; }

/// <summary>
/// Gets or sets the amount of ammo in the firearm magazine.
/// </summary>
Expand Down Expand Up @@ -176,6 +186,57 @@ public int MaxMagazineAmmo
set => PrimaryMagazine.MaxAmmo = value;
}

/// <summary>
/// Gets or sets the damage for this firearm.
/// </summary>
public float Damage
{
get => HitscanHitregModule.BaseDamage;
set => HitscanHitregModule.BaseDamage = value;
}

/// <summary>
/// Gets or sets the inaccuracy for this firearm.
/// </summary>
public float Inaccuracy
{
get => HitscanHitregModule.BaseBulletInaccuracy;
set => HitscanHitregModule.BaseBulletInaccuracy = value;
}

/// <summary>
/// Gets or sets the penetration for this firearm.
/// </summary>
public float Penetration
{
get => HitscanHitregModule.BasePenetration;
set => HitscanHitregModule.BasePenetration = value;
}

/// <summary>
/// Gets or sets how much fast the value drop over the distance.
/// </summary>
public float DamageFalloffDistance
{
get => HitscanHitregModule.DamageFalloffDistance;
set => HitscanHitregModule.DamageFalloffDistance = value;
}

/// <summary>
/// Gets the damage for this firearm with attachement modifier.
/// </summary>
public float EffectiveDamage => HitscanHitregModule.EffectiveDamage;

/// <summary>
/// Gets the inaccuracy for this firearm with attachement modifier.
/// </summary>
public float EffectiveInaccuracy => HitscanHitregModule.CurrentInaccuracy;

/// <summary>
/// Gets the penetration for this firearm with attachement modifier.
/// </summary>
public float EffectivePenetration => HitscanHitregModule.DisplayPenetration;

/// <summary>
/// Gets or sets the amount of max ammo in the firearm barrel.
/// </summary>
Expand Down
76 changes: 34 additions & 42 deletions EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
namespace Exiled.CustomItems.API.Features
{
using System;
using System.Linq;

using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features;
using Exiled.API.Features.DamageHandlers;
Expand All @@ -19,8 +19,7 @@ namespace Exiled.CustomItems.API.Features

using InventorySystem.Items.Firearms.Attachments;
using InventorySystem.Items.Firearms.Attachments.Components;
using InventorySystem.Items.Firearms.BasicMessages;

using InventorySystem.Items.Firearms.Modules;
using UnityEngine;

using Firearm = Exiled.API.Features.Items.Firearm;
Expand Down Expand Up @@ -57,6 +56,8 @@ public override ItemType Type
/// <summary>
/// Gets or sets a value indicating how big of a clip the weapon will have.
/// </summary>
/// <remarks>Warning for <see cref="ItemType.GunShotgun"/> and <see cref="ItemType.GunRevolver"/>.
/// They are not fully compatible with this features.</remarks>
public virtual byte ClipSize { get; set; }

/// <summary>
Expand All @@ -76,9 +77,6 @@ public override ItemType Type
if (!Attachments.IsEmpty())
firearm.AddAttachment(Attachments);

firearm.MagazineAmmo = ClipSize;
firearm.MaxMagazineAmmo = ClipSize;

Pickup? pickup = firearm.CreatePickup(position);

if (pickup is null)
Expand All @@ -87,6 +85,9 @@ public override ItemType Type
return null;
}

if (ClipSize > 0)
firearm.MagazineAmmo = ClipSize;

pickup.Weight = Weight;
pickup.Scale = Scale;
if (previousOwner is not null)
Expand All @@ -104,8 +105,9 @@ public override ItemType Type
if (!Attachments.IsEmpty())
firearm.AddAttachment(Attachments);

if (ClipSize > 0)
firearm.MagazineAmmo = ClipSize;
int ammo = firearm.MagazineAmmo;
firearm.MaxMagazineAmmo = ClipSize;
Log.Debug($"{nameof(Name)}.{nameof(Spawn)}: Spawning weapon with {ammo} ammo.");
Pickup? pickup = firearm.CreatePickup(position);
pickup.Scale = Scale;
Expand All @@ -130,8 +132,8 @@ public override void Give(Player player, bool displayMessage = true)
if (!Attachments.IsEmpty())
firearm.AddAttachment(Attachments);

firearm.MagazineAmmo = ClipSize;
firearm.MaxMagazineAmmo = ClipSize;
if (ClipSize > 0)
firearm.MagazineAmmo = ClipSize;
}

Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker.");
Expand All @@ -144,6 +146,7 @@ public override void Give(Player player, bool displayMessage = true)
protected override void SubscribeEvents()
{
Exiled.Events.Handlers.Player.ReloadingWeapon += OnInternalReloading;
Exiled.Events.Handlers.Player.ReloadedWeapon += OnInternalReloaded;
Exiled.Events.Handlers.Player.Shooting += OnInternalShooting;
Exiled.Events.Handlers.Player.Shot += OnInternalShot;
Exiled.Events.Handlers.Player.Hurting += OnInternalHurting;
Expand All @@ -155,6 +158,7 @@ protected override void SubscribeEvents()
protected override void UnsubscribeEvents()
{
Exiled.Events.Handlers.Player.ReloadingWeapon -= OnInternalReloading;
Exiled.Events.Handlers.Player.ReloadedWeapon -= OnInternalReloaded;
Exiled.Events.Handlers.Player.Shooting -= OnInternalShooting;
Exiled.Events.Handlers.Player.Shot -= OnInternalShot;
Exiled.Events.Handlers.Player.Hurting -= OnInternalHurting;
Expand All @@ -170,6 +174,14 @@ protected virtual void OnReloading(ReloadingWeaponEventArgs ev)
{
}

/// <summary>
/// Handles reloaded for custom weapons.
/// </summary>
/// <param name="ev"><see cref="ReloadedWeaponEventArgs"/>.</param>
protected virtual void OnReloaded(ReloadedWeaponEventArgs ev)
{
}

/// <summary>
/// Handles shooting for custom weapons.
/// </summary>
Expand Down Expand Up @@ -201,49 +213,29 @@ private void OnInternalReloading(ReloadingWeaponEventArgs ev)
if (!Check(ev.Player.CurrentItem))
return;

Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: Reloading weapon. Calling external reload event..");
OnReloading(ev);

Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: External event ended. {ev.IsAllowed}");
if (!ev.IsAllowed)
if (ClipSize > 0 && ev.Firearm.Base.GetTotalStoredAmmo() >= ClipSize)
{
Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: External event turned is allowed to false, returning.");
ev.IsAllowed = false;
return;
}

Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: Continuing with internal reload..");
ev.IsAllowed = false;

int remainingClip = ((Firearm)ev.Player.CurrentItem).MagazineAmmo;
OnReloading(ev);
}

if (remainingClip >= ClipSize)
private void OnInternalReloaded(ReloadedWeaponEventArgs ev)
{
if (!Check(ev.Player.CurrentItem))
return;

Log.Debug($"{ev.Player.Nickname} ({ev.Player.UserId}) [{ev.Player.Role}] is reloading a {Name} ({Id}) [{Type} ({remainingClip}/{ClipSize})]!");

AmmoType ammoType = ev.Firearm.AmmoType;

if (!ev.Player.Ammo.ContainsKey(ammoType.GetItemType()))
if (ClipSize > 0)
{
Log.Debug($"{nameof(Name)}.{nameof(OnInternalReloading)}: {ev.Player.Nickname} does not have ammo to reload this weapon.");
return;
int ammoChambered = ((AutomaticActionModule)ev.Firearm.Base.Modules.FirstOrDefault(x => x is AutomaticActionModule))?.SyncAmmoChambered ?? 0;
int ammodrop = -(ClipSize - ev.Firearm.MagazineAmmo) - ammoChambered;
ev.Firearm.MagazineAmmo = ClipSize - ammoChambered;
ev.Player.AddAmmo(ev.Firearm.AmmoType, (ushort)Mathf.Clamp(ammodrop, ushort.MinValue, ushort.MaxValue));
}

ev.Firearm.Reload();

byte amountToReload = (byte)Math.Min(ClipSize - remainingClip, ev.Player.Ammo[ammoType.GetItemType()]);

if (amountToReload <= 0)
return;

ev.Player.ReferenceHub.playerEffectsController.GetEffect<CustomPlayerEffects.Invisible>().Intensity = 0;

ev.Player.Ammo[ammoType.GetItemType()] -= amountToReload;
ev.Player.Inventory.SendAmmoNextFrame = true;

((Firearm)ev.Player.CurrentItem).MagazineAmmo = (byte)(((Firearm)ev.Player.CurrentItem).MagazineAmmo + amountToReload);

Log.Debug($"{ev.Player.Nickname} ({ev.Player.UserId}) [{ev.Player.Role}] reloaded a {Name} ({Id}) [{Type} ({((Firearm)ev.Player.CurrentItem).MagazineAmmo}/{ClipSize})]!");
OnReloaded(ev);
}

private void OnInternalShooting(ShootingEventArgs ev)
Expand Down
45 changes: 45 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Player/ReloadedWeaponEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// -----------------------------------------------------------------------
// <copyright file="ReloadedWeaponEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Player
{
using API.Features;
using API.Features.Items;

using Interfaces;

/// <summary>
/// Contains all information after a player's weapon is reloaded.
/// </summary>
public class ReloadedWeaponEventArgs : IPlayerEvent, IFirearmEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ReloadedWeaponEventArgs" /> class.
/// </summary>
/// <param name="firearm">
/// <inheritdoc cref="Firearm" />
/// </param>
public ReloadedWeaponEventArgs(InventorySystem.Items.Firearms.Firearm firearm)
{
Firearm = Item.Get<Firearm>(firearm);
Player = Firearm.Owner;
}

/// <summary>
/// Gets the <see cref="API.Features.Items.Firearm" /> being reloaded.
/// </summary>
public Firearm Firearm { get; }

/// <inheritdoc/>
public Item Item => Firearm;

/// <summary>
/// Gets the player who's reloading the weapon.
/// </summary>
public Player Player { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class ReloadingWeaponEventArgs : IPlayerEvent, IFirearmEvent, IDeniableEv
public ReloadingWeaponEventArgs(InventorySystem.Items.Firearms.Firearm firearm)
{
Firearm = Item.Get<Firearm>(firearm);
Firearm.Damage = 99555f;

This comment has been minimized.

Copy link
@not-ivy

not-ivy Jan 20, 2025

magic number?

Player = Firearm.Owner;
}

Expand Down
45 changes: 45 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Player/UnloadedWeaponEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// -----------------------------------------------------------------------
// <copyright file="UnloadedWeaponEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Player
{
using API.Features;
using API.Features.Items;

using Interfaces;

/// <summary>
/// Contains all information after a player's weapon is unloaded.
/// </summary>
public class UnloadedWeaponEventArgs : IPlayerEvent, IFirearmEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="UnloadedWeaponEventArgs" /> class.
/// </summary>
/// <param name="firearm">
/// <inheritdoc cref="Firearm" />
/// </param>
public UnloadedWeaponEventArgs(InventorySystem.Items.Firearms.Firearm firearm)
{
Firearm = Item.Get<Firearm>(firearm);
Player = Firearm.Owner;
}

/// <summary>
/// Gets the <see cref="API.Features.Items.Firearm" /> being unloaded.
/// </summary>
public Firearm Firearm { get; }

/// <inheritdoc/>
public Item Item => Firearm;

/// <summary>
/// Gets the player who's unloading the weapon.
/// </summary>
public Player Player { get; }
}
}
22 changes: 22 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ public class Player
/// </summary>
public static Event<ReloadingWeaponEventArgs> ReloadingWeapon { get; set; } = new();

/// <summary>
/// Invoked after a <see cref="API.Features.Player"/> reloads a weapon.
/// </summary>
public static Event<ReloadedWeaponEventArgs> ReloadedWeapon { get; set; } = new();

/// <summary>
/// Invoked before spawning a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down Expand Up @@ -428,6 +433,11 @@ public class Player
/// </summary>
public static Event<UnloadingWeaponEventArgs> UnloadingWeapon { get; set; } = new();

/// <summary>
/// Invoked after a <see cref="API.Features.Player"/> unloads a weapon.
/// </summary>
public static Event<UnloadedWeaponEventArgs> UnloadedWeapon { get; set; } = new();

/// <summary>
/// Invoked before a <see cref="API.Features.Player"/> triggers an aim action.
/// </summary>
Expand Down Expand Up @@ -837,6 +847,12 @@ public class Player
/// <param name="ev">The <see cref="ReloadingWeaponEventArgs"/> instance.</param>
public static void OnReloadingWeapon(ReloadingWeaponEventArgs ev) => ReloadingWeapon.InvokeSafely(ev);

/// <summary>
/// Called after a <see cref="API.Features.Player"/> reloads a weapon.
/// </summary>
/// <param name="ev">The <see cref="ReloadedWeaponEventArgs"/> instance.</param>
public static void OnReloadedWeapon(ReloadedWeaponEventArgs ev) => ReloadedWeapon.InvokeSafely(ev);

/// <summary>
/// Called before spawning a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down Expand Up @@ -939,6 +955,12 @@ public class Player
/// <param name="ev">The <see cref="UnloadingWeaponEventArgs"/> instance.</param>
public static void OnUnloadingWeapon(UnloadingWeaponEventArgs ev) => UnloadingWeapon.InvokeSafely(ev);

/// <summary>
/// Called after a <see cref="API.Features.Player"/> unloads a weapon.
/// </summary>
/// <param name="ev">The <see cref="UnloadedWeaponEventArgs"/> instance.</param>
public static void OnUnloadedWeapon(UnloadedWeaponEventArgs ev) => UnloadedWeapon.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Player"/> triggers an aim action.
/// </summary>
Expand Down
Loading

0 comments on commit fa82597

Please sign in to comment.