Example of a Gun That Actually Reloads

Handy guides on how to do things, written by users for users.

Moderators: GZDoom Developers, Raze Developers

Forum rules
Please don't start threads here asking for help. This forum is not for requesting guides, only for posting them. If you need help, the Editing forum is for you.
Post Reply
User avatar
FluorescentGreen5
Posts: 11
Joined: Sat Dec 24, 2016 11:26 pm

Example of a Gun That Actually Reloads

Post by FluorescentGreen5 »

Gunlabs' tutorial of weapon reloading demonstrates a weapon that takes from both the main ammo supply and the dummy ammo. Personally I find that a bit crude. Brutal Doom (and other big mods) has a proper reload system, where is only takes from the gun's 'internal' ammo supply and then transfers ammo from the main ammo supply to the gun's 'internal' ammo supply. I have written my own code that does that, and even incorporated the "TIF_NOTAKEINFINITE" flag to allow infinite ammo power-ups to take effect, something that mods with reload don't usually do.

Didn't read the whole tutorial, how silly of me. Anyway, this is just a refined version of GunLab's Weapon Reload Tutorial.

I can understand why some mod makers went with the "count the shots and then inconvenience the player" method, I mean, just look at how complicated this code is:

Here's my code:

Code: Select all

Actor Pistol2 : Pistol //Note that you should use "Pistol2PickUp" for obtaining the weapon.
{
	Weapon.SlotNumber 0 //To make it easier to differentiate it from the original pistol.
	Weapon.AmmoType1 "PistolLoad"
	Weapon.AmmoGive1 20
	Weapon.AmmoUse1 1
	Weapon.AmmoType2 "Clip"
	AttackSound "weapons/pistol"
	
	+WEAPON.AMMO_OPTIONAL //Prevent the weapon from deselecting when running out of ammo, allowing the player to reload it.
	
	States
	{
		Ready:
			PISG A 1 A_WeaponReady(WRF_ALLOWRELOAD)
		Loop
		
		Fire:
			PISG A 0 A_JumpIfNoAmmo("Reload")
			PISG A 4
			PISG B 0 A_FireBullets(5.6, 0, 1, 5, "BulletPuff")
			PISG B 6 A_GunFlash
			PISG C 4
			PISG B 5 A_ReFire
		Goto Ready
		
		Reload:
			TNT1 A 0 A_JumpIfInventory("PistolLoad", 20, "Full")
			TNT1 A 0 A_JumpIfInventory("PowerInfiniteAmmo", 1, "InfiniteReload")
			TNT1 A 0 A_JumpIfInventory("PistolLoad", 1, "TopUpReload")
			TNT1 A 0 A_JumpIfInventory("Clip", 20, 1)
		Goto IncompleteReload
			TNT1 A 35 //Reload Animation
			TNT1 A 0 A_TakeInventory("Clip", 20)
			TNT1 A 0 A_GiveInventory("PistolLoad", 20)
		Goto Ready
		
		TopUpReload:
			TNT1 A 0 A_JumpIfInventory("Clip", 20, 1)
		Goto IncompleteTopUpReload
			TNT1 A 35 //Reload Animation
		UnitTopUpReload:
			TNT1 A 0 A_TakeInventory("Clip", 1)
			TNT1 A 0 A_GiveInventory("PistolLoad", 1)
			TNT1 A 0 A_JumpIfInventory("PistolLoad", 20, "Ready")
		Goto UnitTopUpReload
		
		IncompleteTopUpReload:
			TNT1 A 0 A_JumpIfInventory("Clip", 1, 1)
		Goto NoAmmo
			TNT1 A 35 //Reload Animation
		UnitIncompleteTopUpReload:
			TNT1 A 0 A_TakeInventory("Clip", 1)
			TNT1 A 0 A_GiveInventory("PistolLoad", 1)
			TNT1 A 0 A_JumpIfInventory("Clip", 1, "UnitIncompleteTopUpReload")
		Goto Ready
		
		IncompleteReload:
			TNT1 A 0 A_JumpIfInventory("Clip", 1, 1)
		Goto NoAmmo
			TNT1 A 35 //Reload Animation
		UnitIncompleteReload:
			TNT1 A 0 A_TakeInventory("Clip", 1)
			TNT1 A 0 A_GiveInventory("PistolLoad", 1)
			TNT1 A 0 A_JumpIfInventory("Clip", 1, "UnitIncompleteReload")
		Goto Ready
		
		InfiniteReload:
			TNT1 A 35 //Reload Animation
			TNT1 A 0 A_GiveInventory("PistolLoad", 20)
		Goto Ready
		
		Full:
			PISG A 1 A_Print("No need to reload.")
		Goto Ready
		
		NoAmmo:
			PISG A 1 A_Print("No Ammo")
		Goto Ready
	}
}

Actor PistolLoad : Ammo
{
	Inventory.MaxAmount 20
}

/*
The specialised pickup for "Pistol2". Will give you the gun's internal ammo when you don't have the
gun already, and will give you the main ammo when you already have the gun. Maybe I should rename
the gun to "Pistol2Weapon" and "Pistol2PickUp" to "Pistol2".
*/
Actor Pistol2PickUp : CustomInventory 
{
	Tag "Pistol"
	Inventory.PickupSound "misc/w_pkup"
	Inventory.PickupMessage "$PICKUP_PISTOL_DROPPED"
	
	+INVENTORY.AUTOACTIVATE
	
	States
	{
		Spawn:
			PIST A 1
		Loop
		
		Pickup:
			TNT1 A 0 A_JumpIfInventory("Pistol2", 1, 2) //You could use a state label instead.
			TNT1 A 0 A_GiveInventory("Pistol2")
		Stop
			TNT1 A 0 A_GiveInventory("Clip", 20)
		Stop
	}
}
This code can be simplified, but doing so will create overhead (if you're learning actual programming you'll know that it means it will requires more processing power). But if you don't care, here you go:

Code: Select all

Actor Pistol2 : Pistol //Note that you should use "Pistol2PickUp" for obtaining the weapon.
{
	Weapon.SlotNumber 0 //To make it easier to differentiate it from the original pistol.
	Weapon.AmmoType1 "PistolLoad"
	Weapon.AmmoGive1 20
	Weapon.AmmoUse1 1
	Weapon.AmmoType2 "Clip"
	AttackSound "weapons/pistol"
	
	+WEAPON.AMMO_OPTIONAL //Prevent the weapon from deselecting when running out of ammo, allowing the player to reload it.
	
	States
	{
		Ready:
			PISG A 1 A_WeaponReady(WRF_ALLOWRELOAD)
		Loop
		
		Fire:
			PISG A 0 A_JumpIfNoAmmo("Reload")
			PISG A 4
			PISG B 0 A_FireBullets(5.6, 0, 1, 5, "BulletPuff")
			PISG B 6 A_GunFlash
			PISG C 4
			PISG B 5 A_ReFire
		Goto Ready
		
		Reload:
			TNT1 A 0 A_JumpIfInventory("PistolLoad", 20, "Full")
			TNT1 A 35 //Reload Animation
		UnitReload:
			TNT1 A 0 A_JumpIfInventory("Clip", 1, 1)
		Goto Ready
			TNT1 A 0 A_JumpIfInventory("PistolLoad", 20, "Ready")
			
			TNT1 A 0 A_TakeInventory("Clip", 1, TIF_NOTAKEINFINITE)
			TNT1 A 0 A_GiveInventory("PistolLoad", 1)
		Goto UnitReload
		
		Full:
			PISG A 1 A_Print("No need to reload.")
		Goto Ready
		
		NoAmmo:
			PISG A 1 A_Print("No Ammo")
		Goto Ready
	}
}

Actor PistolLoad : Ammo
{
	Inventory.MaxAmount 20
}

/*
The specialised pickup for "Pistol2". Will give you the gun's internal ammo when you don't have the
gun already, and will give you the main ammo when you already have the gun. Maybe I should rename
the gun to "Pistol2Weapon" and "Pistol2PickUp" to "Pistol2".
*/
Actor Pistol2PickUp : CustomInventory 
{
	Tag "Pistol"
	Inventory.PickupSound "misc/w_pkup"
	Inventory.PickupMessage "$PICKUP_PISTOL_DROPPED"
	
	+INVENTORY.AUTOACTIVATE
	
	States
	{
		Spawn:
			PIST A 1
		Loop
		
		Pickup:
			TNT1 A 0 A_JumpIfInventory("Pistol2", 1, 2) //You could use a state label instead.
			TNT1 A 0 A_GiveInventory("Pistol2")
		Stop
			TNT1 A 0 A_GiveInventory("Clip", 20)
		Stop
	}
}
You will need to change a lot of things in the code to suit your needs.
Let me know if I should add anything to the tutorial.
Last edited by FluorescentGreen5 on Mon May 01, 2017 3:10 am, edited 2 times in total.
User avatar
wildweasel
Posts: 21706
Joined: Tue Jul 15, 2003 7:33 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): A lot of them
Graphics Processor: Not Listed
Contact:

Re: Example of a Gun That Actually Reloads

Post by wildweasel »

FluorescentGreen5 wrote:Gunlabs' tutorial of weapon reloading demonstrates a weapon that takes from both the main ammo supply and the dummy ammo. Personally I find that a bit crude. Brutal Doom (and other big mods) has a proper reload system, where is only takes from the gun's 'internal' ammo supply and then transfers ammo from the main ammo supply to the gun's 'internal' ammo supply
With due respect, I wonder if you read the entire tutorial? While I do Duke style reloads at first, the Gunlabs code does eventually have a proper per-bullet reload system by the end of the tutorial. Unless you meant something else?
User avatar
FluorescentGreen5
Posts: 11
Joined: Sat Dec 24, 2016 11:26 pm

Re: Example of a Gun That Actually Reloads

Post by FluorescentGreen5 »

wildweasel wrote:
FluorescentGreen5 wrote:Gunlabs' tutorial of weapon reloading demonstrates a weapon that takes from both the main ammo supply and the dummy ammo. Personally I find that a bit crude. Brutal Doom (and other big mods) has a proper reload system, where is only takes from the gun's 'internal' ammo supply and then transfers ammo from the main ammo supply to the gun's 'internal' ammo supply
With due respect, I wonder if you read the entire tutorial? While I do Duke style reloads at first, the Gunlabs code does eventually have a proper per-bullet reload system by the end of the tutorial. Unless you meant something else?
It seems I have dun goofed. But then again, the GunLabs tutorial wasn't exactly the cleanest. I will replace what I have with a refined version of their code.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

Re: Example of a Gun That Actually Reloads

Post by Matt »

If we're going to have a reloading tutorial on the first page of this forum can we update it to use countinv and min instead of those loops?

Those 45 lines could be replaced with the following 23:

Code: Select all

      Reload:
         TNT1 A 0 A_JumpIfInventory("PistolLoad", 20, "Full")
         TNT1 A 0 A_JumpIfInventory("PowerInfiniteAmmo", 1, "ReallyReload")
         TNT1 A 0 A_JumpIfInventory("Clip", 1, "ReallyReload")
         Goto NoAmmo
      ReallyReload:
         TNT1 A 35 //Reload Animation
         TNT1 A 0
         {
            //you can replace this with "int tempammocounter..." in zscript
            A_SetInventory("TempAmmoCounter",
                min(
                    20-countinv("PistolLoad"),
                    countinv("Clip")
                )
            );
            A_GiveInventory("PistolLoad", countinv("TempAmmoCounter"));
            if (!countinv("PowerInfiniteAmmo"))
            {
                A_TakeInventory("Clip", countinv("TempAmmoCounter"), TIF_NOTAKEINFINITE);
            }
         }
         Goto Ready
plus 5 lines for the following counter which could be reused for other weapons:

Code: Select all

actor TempAmmoCounter : Inventory
{
    inventory.maxamount 1000000
    //or whatever the highest number is that you need
}
User avatar
phantombeta
Posts: 2084
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

Re: Example of a Gun That Actually Reloads

Post by phantombeta »

Here's something way better for ZScript, although it shouldn't be impossible to port to DECORATE.

Code: Select all

    Reload:
        TNT1 A 0 {
            if (CheckInventory (invoker.ammoType1, 0) || !CheckInventory (invoker.ammoType2, 1))
                return ResolveState ("Ready");

            int ammoAmount = min (FindInventory (invoker.ammoType1).maxAmount - CountInv (invoker.ammoType1), CountInv (invoker.ammoType2));
            if (ammoAmount <= 0)
                return ResolveState ("Ready");

            GiveInventory (invoker.ammoType1, ammoAmount);
            TakeInventory (invoker.ammoType2, ammoAmount);

            return ResolveState ("ReloadFinish");
        }
With this you don't even need to change anything in the code for different weapons. You could even just shove it into a base weapon class and use the same code for every weapon.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

Re: Example of a Gun That Actually Reloads

Post by Matt »

I don't think FindInventory would be available in Decorate, given neither variables nor even "invoker" are, so the counter item and manually inserting the correct ammo class names would still be required.

(but that is an excellent solution and anyone using ZScript should use that instead - I would, however, call an A_SetInventory or whatever to make sure that the FindInventory...maxAmount wouldn't crash)
User avatar
ramon.dexter
Posts: 1520
Joined: Tue Oct 20, 2015 12:50 pm
Graphics Processor: nVidia with Vulkan support
Location: Kozolupy, Bohemia

Re: Example of a Gun That Actually Reloads

Post by ramon.dexter »

phantombeta wrote:Here's something way better for ZScript, although it shouldn't be impossible to port to DECORATE.

Code: Select all

    Reload:
        TNT1 A 0 {
            if (CheckInventory (invoker.ammoType1, 0) || !CheckInventory (invoker.ammoType2, 1))
                return ResolveState ("Ready");

            int ammoAmount = min (FindInventory (invoker.ammoType1).maxAmount - CountInv (invoker.ammoType1), CountInv (invoker.ammoType2));
            if (ammoAmount <= 0)
                return ResolveState ("Ready");

            GiveInventory (invoker.ammoType1, ammoAmount);
            TakeInventory (invoker.ammoType2, ammoAmount);

            return ResolveState ("ReloadFinish");
        }
With this you don't even need to change anything in the code for different weapons. You could even just shove it into a base weapon class and use the same code for every weapon.
This is just sweet! Thank you! :wub:
Post Reply

Return to “Tutorials”