RRWM reloading system [1.3.0a] [ZS/DEC/SBARINFO]

Post your example zscripts/ACS scripts/etc here.

RRWM reloading system [1.3.0a] [ZS/DEC/SBARINFO]

Postby Player701 » Fri Nov 06, 2020 11:00 am

RRWM reloading system

Nutshell

This is a standalone version of the weapon reloading system used in my gameplay mod RRWM.
It requires a custom HUD to display the magazine amount. (either ZScript or SBARINFO, see p. 5 further below for details)

Requires GZDoom 4.3.1+, in earlier versions this may not work properly or at all.

Here are some particular features of this implementation:

  • Weapon does not need the AMMO_OPTIONAL flag. When there is no ammo, it won't be possible to select it unless "check ammo for weapon switch" is disabled in gameplay options (this is vanilla GZDoom behavior).
  • No need to perform ammo checks manually.
  • Ammo in weapon's magazine (aka clip) is contained within the weapon itself, and cannot be tampered with by inventory functions or cheats.
  • Dropped weapon retains the ammo loaded into its magazine. Weapons dropped on death also preserve their magazine contents.
  • Supports alternate attack using the same or a different ammo type. When using a different ammo type, alternate attack is not subject to the reloading feature.
  • Additional ways to reload the weapon besides using the reload button (see p. 4.1 further below for details).
  • Proper handling of the infinite ammo cheat/powerup (though you still have to reload the weapon periodically).

Download the latest release (1.3.0a) - Based on RRWM 1.3.0a released on 2020-11-27
Bitbucket repository

NB: This code is provided as-is and will not receive feature updates unless they come from upstream (the RRWM Bitbucket repository). However, if you find a bug, please report it here and I'll try to fix the code and provide an updated version. You can also report bugs via the RRWM issue tracker. Click here to report a bug via the issue tracker.


How to use

Using this system in your own project requires only minimal experience with ZScript, and only if you want to fine-tune the reloading mechanics (see pp. 4.1-4.3 for details). You can use DECORATE for your classes and SBARINFO for your HUD. However, ZScript gives you more advantages such as the ability to customize your weapon even further by overriding the provided virtual methods (see "Additional virtual methods" further below for more information).


1) Make an ammo class derived from ReloadableAmmo

All ammo types for your reloadable weapons must be derived from ReloadableAmmo. This class contains some logic to work around a certain special case in the weapon auto-switching behavior. You can use either ZScript or DECORATE to define your ammo class.


2) Make a weapon class derived from ReloadableWeapon

You can use either ZScript or DECORATE to define your weapon class. This is how you should set it up:

  1. Set an ammo type derived from ReloadableAmmo.
  2. Set AmmoUse to a positive value.
  3. Set ReloadableWeapon.ClipCapacity to a positive value and a multiple of AmmoUse.
  4. Instead of calling A_WeaponReady in your Ready state, you should call A_ReloadableWeaponReady.
  5. Define a full reload state or a sequential reload state (see p. 2.1 or p. 2.2).
  6. Optionally, define a dry fire state (see p. 3).

A_ReloadableWeaponReady optionally accepts the same flags as A_WeaponReady does, except for WRF_ALLOWRELOAD, which is obviously redundant.

NB: Weapon flags PRIMARY_USES_BOTH and ALT_USES_BOTH are not supported.


2.1) Add a full reload state

If your weapon has a single animation depicting a complete reload (magazine swap), you should define a full reload state. Just add a Reload state to the weapon, put your animation in there, and at the point where the actual reloading should happen, call A_ReloadFullClip. You should return to the Ready state at the end of your reloading animation.


2.2) Add a sequential reload state

If your weapon is reloaded round by round (e.g. like a shotgun), you should define a sequential reload state. Your Reload state sequence must be designed in the following way:

  1. The sequence must run in a loop.
  2. Call A_ReloadOneRound anywhere in your state sequence when you want to load the amount of ammo equal to AmmoUse1.
  3. Call A_CheckReloadDone anywhere in your state sequence where you want your weapon to jump out of it when there is no more ammo to load.

You can also add a ReloadDone state where you could put some animation depicting your weapon finishing the reloading sequence. The weapon will jump to the ReloadDone state when A_CheckReloadDone is called and there is no more ammo left to load. If there is no ReloadDone state, it will jump to the Ready state instead.

Optionally, if you want your reloading sequence to be interruptible, add a call to A_ReloadableWeaponReady anywhere after the call to A_ReloadOneRound. This enables the reloading sequence to be interrupted in the following ways:

  1. The weapon can be deselected, and will jump to the DeselectReload state upon a weapon change request. If there is no DeselectReload state, it will jump to the Deselect state instead.
  2. The weapon can be fired, and will jump to the FireReload state upon pressing the fire button. If there is no FireReload state, it will jump to the Fire state instead. If alternate fire is supported, the same logic is used to jump to either AltFireReload or AltFire states.
  3. The reloading sequence can be terminated prematurely by pressing the reload button again. The weapon will jump to the ReloadDone state (or the Ready state, if ReloadDone is not defined) as soon as A_CheckReloadDone is called next time.


3) Optional: Add dry fire states

You can make your weapon do something (e.g. play a clicking sound) when the player attempts to fire it with no ammo. This can happen under different circumstances, for example:

  • The weapon runs out of ammo while firing, and the player keeps holding the fire button.
  • The reloading mode is set to manual, and the player tries to fire the weapon. See p. 4.1 for more information about reloading modes.

To add an animation to your weapon that will be displayed when players try to fire it while empty, define a DryFire state. For alternate fire, define a DryAltFire state instead (both states can be defined at the same time). Put your animation in this state. Now there are only two things left to take care of:

  1. Make sure you have a call to A_ReFire somewhere in your main fire / alt-fire state.
  2. Add a call to A_DryReFire to the end of your dry fire state sequence.

Now you're all set. When A_ReFire is called next time and the weapon is out of ammo, it will jump to its DryFire or DryAltFire state. A call to A_DryReFire ensures that the weapon gets deselected as soon as the fire button is released, provided that reloading or using an alternate attack is not possible.

There are two additional dry fire state labels that can be made use of:

  1. Dry[Alt]Hold: If defined, the weapon will jump to this state instead of Dry[Alt]Fire when A_DryReFire is called and the fire button is still held.
  2. Dry[Alt]FireCooldown: If defined, the weapon will jump to this state instead of Dry[Alt]Fire when it runs out of ammo while firing. The weapon will jump to the Dry[Alt]Fire state as usual if it's already out of ammo when the player attempts to fire it. This can happen if "check ammo for weapon switch" is disabled or if the reloading mode is set to manual (see p. 4.1 directly below for more information).


4.1) Optional setup: Reloading mode

This reloading system supports three reloading modes: full auto, semi-auto, and manual. The mode determines what exactly the player can do to reload the weapon. Depending on the mode, the following options may be available:

  1. Using the reload button. This is obviously available in all modes.
  2. Attempting to fire the weapon with an empty magazine when there is enough ammo to reload. This is available in full auto and semi-auto modes.
  3. Automatic reload of an empty weapon after a certain time delay (see further below for how to adjust it). This is only available in full auto mode.

By default, the mode is set to full auto. To change it, search for the first appearance of "GetReloadMode" in classes/core/ReloadableWeapon.zs and edit the return value. The value must be either RM_FULLAUTO, RM_SEMIAUTO, or RM_MANUAL. It is not a requirement that the mode remains the same for the duration of the game. You can make it depend on a CVar, which is exactly what RRWM itself does.


4.2) Optional setup: Auto-reload delay (only for full auto reloading mode)

If the reloading mode is set to full auto, an empty weapon will reload itself automatically after a certain time delay. This delay can be adjusted by changing the value of the AUTORELOAD_DELAY constant in ReloadableWeapon. Search for the first appearance of "AUTORELOAD_DELAY" in classes/core/ReloadableWeapon.zs (it's near the very top of the class definition) and change the value to your own preference.


4.3) Optional setup: Auto-reload ammo level threshold (only for full auto reload mode)

If the reloading mode is set to full auto, you can make the weapon initiate reloading automatically only when there is a certain amount of ammo available in reserve. This amount is expressed as a percentage of the weapons's magazine capacity. For example, if your weapon has a magazine capacity of 10 and you've set the threshold to 30%, the weapon will only reload automatically when you have 10 * 30% = 3 or more rounds left in reserve. Default value is 0, which means the weapon will always initiate an automatic reload if the player has at least AmmoUse1 ammo in reserve.

To change the threshold value, search for the first appearance of "GetAutoReloadThreshold" in classes/core/ReloadableWeapon.zs and edit the return value. Note that it is a percentage (goes from 0 to 100) and not a fraction (goes from 0 to 1). It is not a requirement that the value remains constant: RRWM also ties it to a user CVar, and you can do the same if you wish.


5) Implement a custom HUD with a magazine counter

You are encouraged to use a ZScript-based HUD to display the clip amount. However, SBARINFO-based HUDs are suported as well. To use an SBARINFO-based HUD, you have to do the following setup:

  1. Include all classes from the example package subfolder "classes/core/sbarinfo_support" into your mod.
  2. Add MagazineCheck to the starting inventory of your player class. The example package already includes a player class called SbarinfoSupportPlayer with a correctly set up starting inventory. You will also have it when you include the SBARINFO support classes, so you can just modify it instead of creating a new class from scratch.

To draw the clip amount, use the DrawNumber SBARINFO command with MagazineCounter as an argument. You should place this command in a conditional block so that it is only displayed when the player's current weapon is reloadable. This can be done with the following code:

Code: Select allExpand view
InInventory MagazineCheck, 1
{
    // This block is executed if the player's current weapon is reloadable.
    // Adjust the parameters here to your liking.
    drawnumber 3, HUDFONT_DOOM, untranslated, MagazineCounter, 44, 171;
}


The example package provides an SBARINFO that implements the standard Doom HUD (based on the corresponding SBARINFO file from gzdoom.pk3) and can also display the magazine counter for reloadable weapons. The counter is available in both the status bar and the fullscreen HUD.

ZScript coders should use ReloadableWeapon::GetClipAmount to display the counter in their HUDs. This method is available for use in the UI context. Don't forget to check that the player's ReadyWeapon is actually a ReloadableWeapon before attempting to draw the counter. The example package includes a bare-bones ZScript HUD with the clip and ammo counters only. Uncomment the StatusBarClass line in MAPINFO and switch to the fullscreen HUD in-game to witness the example ZScript HUD in action.


Additional virtual methods

For advanced coders, ReloadableWeapon provides several virtual methods that can be overridden in derived classes.


Code: Select allExpand view
protected virtual state GetSelectState();

Use this method instead of GetUpState if you need to choose a select state based on an external factor (e.g. a fire mode).


Code: Select allExpand view
protected virtual void PreRaise();

Use this method when you need to initialize or reset the internal state of your weapon before it is raised. This is called just before GetSelectState.

NB: You must ensure this method is idempotent, i.e. calling it multiple times in a row produces the same effect as calling it just once. This is because in most cases, this method is called twice upon a level change - first when the level actually changes, and then when the weapon is brought up - but the latter is not guaranteed to happen in all cases.


Code: Select allExpand view
protected virtual state GetDeselectState(bool fromReload);

Use this method instead of GetDownState if you need to choose a deselect state based on an external factor. fromReload is true when the weapon is being deselected from an interrupted reloading sequence (see p. 3.2 for details).


Code: Select allExpand view
protected virtual void PreLower();

Use this method when you need to adjust the internal state of your weapon before it is lowered. This is called just before GetDeselectState.


Code: Select allExpand view
protected virtual state GetFireState(bool altFire, EFireType type);

Use this method instead of GetAtkState or GetAltAtkState if you need to choose a fire state based on an external factor. altFire is true when the player is using the alternate fire button instead of the normal one. type is equal to one of the following values:

  • FT_Normal - normal fire
  • FT_Hold - weapon is returning to the fire state after a call to A_ReFire
  • FT_FromReload - weapon is firing from an interrupted reloading sequence (see p. 3.2 for details).


Code: Select allExpand view
protected virtual state GetDryFireState(bool altFire, EDryFireType type);

Similar to GetFireState, you can override this method to choose your own dry fire state. type is equal to one of the following values:

  • DFT_Normal - normal dry fire
  • DFT_Hold - weapon is returning to the dry fire state after a call to A_DryReFire
  • DFT_Cooldown - weapon is entering the dry fire state having run out of ammo while firing.


Code: Select allExpand view
protected virtual state GetReloadState();

Override this method to choose your own reload state.


Code: Select allExpand view
protected virtual state GetReloadDoneState();

Override this method to choose your own state for finishing a sequential reload (see p. 3.2 for details). If this method returns null, the weapon will jump to its ready state instead.


Under the hood

The RRWM reloading system relies heavily on the setup of interaction between the internal classes PlayerPawn and Weapon. The exact behaviors that it leverages are undocumented but so is most of the code in gzdoom.pk3, so this isn't considered an issue. It is unlikely that the system will break in newer GZDoom versions, since it has already survived several major GZDoom releases without any fundamental changes.

Do note, however, that altering any aspect of the system's behavior might not be a trivial task. It was not designed to be constantly modified and expanded because its exact goals had been outlined from the very beginning. This code does exactly what it was supposed to do; the system's core features have been playtested for countless hours. It is still not guaranteed to be bug-free, though, especially considering that some of the optional features have been added mostly for completeness' sake and are not used extensively (if at all) in RRWM itself.


Flowchart

Here you can find the complete flowchart of events that can happen when the player is using a reloadable weapon, depending on the weapon's state and the player's actions. The chart should be read from the top left. Download the image to your PC to zoom in. The flowchart is also included in the release archive.


Examples

The release package contains two example weapons, implemented in DECORATE. The weapons are included in the player's starting inventory.

  • DukeShotgun (slot 3): A simple reloadable shotgun.
  • ZenGun (slot 4): An SMG with a normal and an alternate attack.

These weapons use third-party resources that come directly from RRWM. Please see the RRWM credits file for more information about the origin of the resources.


Crediting

You can use this code in your own projects and modify it as you see fit. But you also have to credit the author as Player701 and mention that you are using the RRWM reloading system.

Thank you very much!
User avatar
Player701
 
 
 
Joined: 13 May 2009
Location: Russia
Discord: Player701#8214
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: nVidia with Vulkan support

Re: RRWM reloading system [1.3.0a] [ZS/DEC/SBARINFO]

Postby Player701 » Fri Nov 27, 2020 11:32 am

RRWM reloading system has been updated to version 1.3.0a. Download the new release HERE. The link in the first post has been updated too.

In terms of functionality, this version is completely identical to 1.2.3, but the PK3 file now uses a more common compression algorithm to make it easier to work with. Most importantly, this means that you shouldn't get any more problems trying to open the package and examine its contents with editing tools like SLADE. Also, the release archive is now provided in ZIP format instead of the less popular 7Z, so that you don't have to install any third-party software to unpack it.
User avatar
Player701
 
 
 
Joined: 13 May 2009
Location: Russia
Discord: Player701#8214
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: nVidia with Vulkan support


Return to Script Library

Who is online

Users browsing this forum: No registered users and 0 guests