TUTORIAL: An Explanation of the "Half-Tic" Trick

In my recent ZDoom-based mod, Police Brutality: Weasel Presents Terrorists!, I experimented with some subtle effects on weapons. I'm not talking about the upgrade system; that's anything but subtle and beyond the scope of this tutorial. (Maybe if there's enough demand I'll explain it in more depth.) No, the effect in question is what I like to call the "half-tic" - though it might be more accurate to call it a "sub-tic" as it's not actually measured by the in-game tic counter.

I'll take my explanation from the beginning. As you might know already, Doom (and by extension, ZDoom) runs the game at 35 tics per second. Now, this was nice and smooth back in 1993 when it first came out, and it's still at least five frames a second faster than more contemporary modern games (*cough* Halo *cough*), but for more precise timing, it might be tricky to work with.

Let's take, for example, a simple machine gun. I tend to measure the fire rates of my machine guns by the number of tics between individual bullets. For example, Doom's chaingun is a "4-tic" refire, as it fires one bullet, waits 4 tics, then fires another, until the fire button is finally released. Strife's assault rifle is a 3-tic refire, which is noticeably faster than the chaingun. Skulltag's minigun is a 2-tic refire - by now we're actually firing at double the speed of Doom's chaingun.

Actual machine guns are measured in rounds per minute (RPM), which is a much more precise unit of measurement, but it'll take some number crunching to pare down the tic-rates into RPM rates. So let's figure that out right now, using the known fire rate of a real gun - we'll say a Colt M4A1 - which according to my preferred firearms guide, Modern Firearms & Ammunition, ranges from 700-950 RPM. We'll stick with 700 as our baseline.

So we know Doom's chaingun fires once every 4 tics, and Doom runs at 35 tics per second. We'll take 35 - which represents one second - and divide it by 4, getting 8.75 shots per second. Multiply that by 60, and we get the per-minute count, which is a very modest 525 RPM, which is more akin to, say, a Browning M2 or a Bren gun.

Let's do that again with Strife's assault rifle:
35 tics per second / 3 tic refire delay = ~11.66666667 * 60 seconds = 700 RPM.
That means Strife's assault rifle is just on the low end of what an actual assault rifle can manage, as far as cyclic rate.

And one more time with Skulltag's minigun:
35 TPS / 2-tic refire = 17.5 RPS * 60 secs = 1,050 RPM, or noticeably faster than the quickest-cycling M4A1, and exactly double that of the chaingun.

As you can see, one tic of refire makes a load of difference, so if you want your guns to fire at a rate somewhere in between the three "extremes", you're going to need a time unit more precise than a tic. But Doom doesn't have any more precise ways to measure time, you're probably thinking. That's where I came up with a special trick that I called the "half-tic." I was actually inspired by tasvideos.org's guide to Sonic the Hedgehog's physics, which goes into great detail about how these 16-bit platformers do not measure player movement in raw pixels, but rather "sub-pixels," so that a greater precision may be exacted over player movement, resulting in much smoother speed changes.

So how would I handle something similar with tics? I notice that the article is actually not storing the player's location in sub-pixels, but as a raw x/y pixel coordinate coupled with two sub-pixel counters for x and y. Well, since Doom's ticker is a strictly linear affair, I only need one such counter, and for simplicity's sake, I really only need it to be a boolean, i.e. 1 or 0.

Let's have a look at Doom's chaingun Fire state.
Fire:
     CHGG AB 4 A_FireCGun
     CHGG B 0 A_ReFire
     Goto Ready
Pretty simple; fires one bullet every 4 tics as mentioned before. Well, here's what I'm going to add to this. First, a sub-tic counter.
ACTOR HalfTicCounter : Inventory { Inventory.MaxAmount 1 }
(Oh, did I forget to show you how you can condense simple actors to just one line? Sometimes you can do that. Not often, but sometimes.)

Now, this might get a little cluttered. It was recently suggested on the ZDoom forums that one could use the A_SetTics function in Decorate to handle this, but I do not think that you can use the value of an inventory item in a Decorate expression, so we're going to go do this the hard way, with A_JumpIfInventory.

Fire:
   CHGG A 0 A_JumpIfInventory("HalfTicCounter", 1, "FireHalftic")
   CHGG A 4 A_FireCGun
   CHGG A 0 A_GiveInventory("HalfTicCounter", 1)
   Goto Ready
FireHalftic:
   CHGG A 0 A_TakeInventory("HalfTicCounter", 1)
   CHGG B 3 A_FireCGun
   Goto Ready

So here's what it's doing: the weapon checks the half-tic counter first, then having failed the check, fires at the "normal" 4-tic rate on the first shot and adds to the counter. When the next shot is fired, it will pass the half-tic check and go to the FireHalftic state instead, where the weapon fires at the quicker 3-tic rate, then takes the counter away again. This effectively sets the weapon's fire rate at 3.5 tics per shot, or going by our formula again:
35 TPS / 3.5 tics = 10 RPS * 60 secs = 600 RPM, or roughly halfway in between the chaingun's 525 RPM and the Strife assault gun's 700.

Now, there is a drawback, for those with sensitive ears: since this is obviously not a "true" fire rate (this would not be the case if Doom ran at 70 FPS, where a fire rate of 7 tics per shot is more natural), the sound made when firing the gun at this rate may seem to stutter, or fire at an uneven rate. With faster fire rates, this is less noticeable, but it's honestly a small price to pay for the added precision involved. You could potentially get around this issue by perhaps replacing your weapon's firing sound with a looping one, similar to the ones used in more modern games like Far Cry or Rainbow Six Vegas.

I imagine half-tics could easily be expanded into different ranges of sub-tics to achieve different fractions; by increasing the maximum value of the half-tic counter to 2, you could have an extra 1/3-tic, or even beyond.

(Weasel's Note: Regarding the example I used above, I know I could easily just write it like this...)
Fire:
   CHGG A 4 A_FireCGun
   CHGG B 3 A_FireCGun
(But that's not really the point of the tutorial, is it?)

TUTORIAL: New Ammo Types and Weapons That Reload

It's been asked of me for ages..."Weasel, when are you going to write a tutorial about reloading weapons?" Well, now that ZDoom's SVNs have effectively made a "canon" reload key, I feel more comfortable about teaching people how to use it.

A note before you begin: ZDoom's Reload feature was only made official as of SVN r3530 (which you can get here). For users of ZDoom-derivative ports that are not updated to this SVN revision (i.e. Zandronum), I'll have an extra section below, where I describe the "old" method of reloading that requires CustomInventory items.

So for reference, here is a basic weapon, derived from Doom's pistol with a couple of changes to make it more obvious what we're doing.

actor NewPistol : Weapon
{
 Weapon.SelectionOrder 1900
 Weapon.AmmoUse 1
 Weapon.AmmoGive 20
 Weapon.AmmoType "Clip"
 Inventory.Pickupmessage "Picked up a pistol."
 States
 {
 Ready:
  PISG A 1 A_WeaponReady
  Loop
 Deselect:
  PISG A 1 A_Lower
  Loop
 Select:
  PISG A 1 A_Raise
  Loop
 Fire:
  PISG A 4
  PISG B 6 A_FireBullets(4,4,-1,6,"BulletPuff",1)
  PISG C 4
  PISG B 5 A_ReFire
  Goto Ready
 Flash:
  PISF A 3 Bright A_Light2
  PISF A 3 Bright A_Light1
  PISF A 0 Bright A_Light0
  Goto LightDone
 Spawn:
  PIST A -1
  Stop
 }
}

As it stands, this code does work. However, at the moment, it is firing ammo directly from the player's inventory of bullets (the "Clip" ammo actor). This is perhaps the simplest possible variant of this sort of weapon. But what we want to do is make it reload - more accurately, we want the weapon to keep track of how much ammo is loaded in it, and require the player to pause for a moment to put more into it. And since it occurs to me that I haven't written a tutorial on ammo types before, I'll cover that, too.

ACTOR 9mmAmmo : Ammo replaces Clip
{
 Inventory.Amount 10 // You'll get half this if a monster drops it.
 Inventory.MaxAmount 150
 Ammo.BackpackAmount 30 // How much you'll get out of backpacks or equivalent.
 Ammo.BackpackMaxAmount 300 // The new max amount if player has a backpack.
 Inventory.Icon "CLIPA0" // This displays on the fullscreen HUD.
 Inventory.PickupMessage "Picked up some 9mm ammo."
 States
 {
 Spawn:
  CLIP A -1
  Stop
 }
}

The comments in the code above should make the inner workings of an ammo type fairly obvious. There are a number of things I haven't mentioned above, though. If you decide you want to make a larger (or smaller) version of the ammo pickup, like Doom's Box of Bullets, you'd inherit from the previous ammo type instead of just from Ammo, like so:

ACTOR 9mmAmmoSmall : 9mmAmmo
{
 Inventory.Amount 6
 Inventory.PickupMessage "Picked up a bit of 9mm ammo."
 Inventory.PickupSound "Beretta/Drop"
 +DROPPED
 States
 {
 Spawn:
  9MMP A -1
  Stop
 }
}

Notice the +DROPPED flag? The ZDoom Wiki has this to say about it: "Actor always acts as if it was dropped. Dropped items have two properties: They never respawn, and they will be crushed by doors and moving sectors." In this case, it also has the effect that it will give you half of the specified Inventory.Amount, similarly to how dropped Chainguns only give you 10 bullets instead of the 20 you'd get if you found one that was deliberately placed in a map. Since 9mmAmmoSmall inherits from 9mmAmmo, instead of defining a whole new ammo type, ZDoom treats it as an alternate pickup for the base type, so you get 9mmAmmo from picking this new item up.

So you can go ahead and change the NewPistol to use this new ammo type:

Weapon.AmmoType "9mmAmmo"

But we're not finished. We're here to make a gun that reloads, right? Let's define a whole new ammo type then:

ACTOR NewPistolLoaded : Ammo
{
 Inventory.MaxAmount 12
 +IGNORESKILL
}

Since you aren't going to be picking this ammo type up, there is a lot less that we need to do with it, code-wise. However, note another flag I've added, +IGNORESKILL. There is a special context to this flag. When you play Doom on Nightmare! or I'm Too Young To Die skill level, all ammo pickups are worth double what they usually give. +IGNORESKILL prevents this from happening on a per-ammo basis. This is important for our gun, for reasons I'll get into once we write our reloading code.

The easiest reloading system we can do is something Duke Nukem 3D-styled - this style of reloading does not require a Reload key and only keeps track of how many shots have been fired from the current weapon. In order to implement this, we'll need a new Reload state in the gun, and two simple lines in the Fire state.

Your reload state only really needs one important line for housekeeping purposes:

Reload:
  PISG A 1 A_TakeInventory("NewPistolLoaded", 12)
  Goto Ready

Now, in our case, we don't really need to give the amount parameter here; if A_TakeInventory is called with no amount specified (i.e. A_TakeInventory("NewPistolLoaded")), ZDoom will interpret this as a command to take ALL of that item. So really, take it or leave it.

This state will make more sense once you see how we're altering the Fire state:

Fire:
  PISG A 0 A_JumpIfInventory("NewPistolLoaded", 12, "Reload")
  PISG A 4 A_GiveInventory("NewPistolLoaded", 1)
  PISG B 6 A_FireBullets(4,4,-1,6,"BulletPuff",1)
  PISG C 4
  PISG B 5 A_ReFire
  Goto Ready

It's important to know how ZDoom's A_JumpIfInventory works. It only checks if the player has at least as many as specified. Using A_JumpIfInventory, it is not directly possible to check if a player has less than the specified amount, nor is it possible to check for the player to have none of an amount. (It is possible, however, to run your check "backwards": if you want to proceed only if the player has none of an item, you can check for the item, and should the check fail, it would fall through to a state that would behave as if there were no item. I will be making use of this later.) Therefore, the ammo type that we've called "NewPistolLoaded" is actually keeping track of how many shots have been fired, not how many are in the gun. Attempting to check for 0 of an inventory item is actually interpreted as checking for the maximum of that item, so in this case you could actually change the 12 to a 0 and have the same functionality should you later decide to change the weapon's magazine size (i.e. the MaxAmount of NewPistolLoaded).

One other thing. Remember that +IGNORESKILL flag we added to NewPistolLoaded? That plays into the A_GiveInventory function - if the player is playing on a skill setting that affects ammo pickups, this also affects the giving of ammo items through weapon code and scripts. This means that players playing with this weapon on skill 1 or 5 will only be able to fire 6 shots from this pistol before needing to reload, since A_GiveInventory is doubled on those skill levels. Not the most desirable thing in the world, so +IGNORESKILL is added to that ammo type to prevent that from happening.

The code as it stands isn't really doing anything noticeable, so let's add a cheap-ass reloading animation to the Reload state.

Reload:
  PISG A 1 Offset(0,35) A_PlayWeaponSound("weapons/shotgr") // click-clack.
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,82)
  TNT1 A 8 A_TakeInventory("NewPistolLoaded", 12)
  PISG A 1 Offset(0,82)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,35)
  PISG A 1 Offset(0,32)
  Goto Ready

I believe I already went on about Offsets in How to Make Your First Basic Gun.

So here's where our weapon is at so far:

actor NewPistol : Weapon
{
 Weapon.SelectionOrder 1900
 Weapon.AmmoUse 1
 Weapon.AmmoGive 20
 Weapon.AmmoType "9mmAmmo"
 Weapon.SlotNumber 2 // Forgot to put this in earlier...
 Inventory.Pickupmessage "Picked up a pistol."
 States
 {
 Ready:
  PISG A 1 A_WeaponReady
  Loop
 Deselect:
  PISG A 1 A_Lower
  Loop
 Select:
  PISG A 1 A_Raise
  Loop
 Fire:
  PISG A 0 A_JumpIfInventory("NewPistolLoaded", 12, "Reload")
  PISG A 4 A_GiveInventory("NewPistolLoaded", 1)
  PISG B 6 A_FireBullets(4,4,-1,6,"BulletPuff",1)
  PISG C 4
  PISG B 5 A_ReFire
  Goto Ready
 Reload:
  PISG A 1 Offset(0,35) A_PlaySound("weapons/shotgr", CHAN_WEAPON) // click-clack.
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,82)
  TNT1 A 8 A_TakeInventory("NewPistolLoaded", 12)
  PISG A 1 Offset(0,82)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,35)
  PISG A 1 Offset(0,32)
  Goto Ready
 Flash:
  PISF A 3 Bright A_Light2
  PISF A 3 Bright A_Light1
  PISF A 0 Bright A_Light0
  Goto LightDone
 Spawn:
  PIST A -1
  Stop
 }
}

This pistol will fire 12 shots, and on the 12th, will dip off screen and play clicky noises. When it comes back up, it will be "loaded" again. Congratulations, you've made a weapon that reloads.

But wait, I hear you say, what about those modern games that let you reload in the middle of a clip by pressing a button? Well, there's another neat function you can use - and here's the great bit, it's part of a function you've already been using since the very first tutorial! (Zandronum users, you may ignore this and look at the special section later in the tutorial.)

Ready:
  PISG A 1 A_WeaponReady(WRF_ALLOWRELOAD)
  Loop

That's right, A_WeaponReady accepts flags! If you don't specify a flag for A_WeaponReady (you usually don't need to), the default behavior is to check for presses of the Fire or Altfire buttons and go to the appropriate state. What this WRF_ALLOWRELOAD flag does, though, is add an extra check for a third button: Weapon Reload. Go check out your Customize Controls menu in ZDoom. Right up there with your Fire and Alternate Fire buttons are two new keys: Weapon Reload and Weapon Zoom. Reload is the one we want for now. With that flag in place, ZDoom checks for presses of the Weapon Reload key and redirects the weapon to the Reload state. (Hey, didn't we actually call our state Reload? That means you can press Reload with your mod loaded and reload mid-clip!)

Now that we've figured out how to reload mid-clip, there are actually two other reload systems we can use. The first, and simpler to code, is a system that takes entire clips. The second counts individual bullets. We'll cover the first system first, of course.

First, let's modify the ammo types for the current weapon, so the correct ammo type is taken when the weapon is fired (and so the reserve ammo is still displayed on screen). For this system (and the next one), we're using the NewPistolLoaded as the primary ammo type, and the ammo pickup as the secondary.

Weapon.AmmoType1 "NewPistolLoaded"
 Weapon.AmmoUse1 1
 Weapon.AmmoGive1 0 // AmmoGive is actually 0 by default, so this isn't strictly necessary.
 Weapon.AmmoType2 "9mmAmmo"
 Weapon.AmmoUse2 0
 Weapon.AmmoGive2 20

You should pay particular care that the "in clip" ammo (NewPistolLoaded, in this case) is not given from weapon pickups, so that the weapon does not magically reload itself when you pick up another one. (If you want the weapon to start off fully-loaded instead of empty, give it to the player via Player.StartItem in a custom player class, or via an ACS script. If you didn't understand that sentence, don't worry about it.)

We'll obviously also need to alter our Reload state, otherwise the first shot from this weapon will completely empty it and you'll never be able to fire it.

Reload:
  PISG A 1 Offset(0,35) A_PlaySound("weapons/shotgr", CHAN_WEAPON) // click-clack.
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,82)
  TNT1 A 0 A_GiveInventory("NewPistolLoaded", 12)
  TNT1 A 8 A_TakeInventory("9mmAmmo", 1) // We'll treat one 9mmAmmo as one full clip.
  PISG A 1 Offset(0,82)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,35)
  PISG A 1 Offset(0,32)
  Goto Ready

You will obviously also want to remove the A_GiveInventory and A_JumpIfInventory from the Fire state. Go ahead and test it. You should notice two major issues with the code as it stands. Firstly, you can continuously reload your gun, even if it's full, and waste 9mmAmmo in the process. Second, if the weapon is empty, it auto-switches to a different weapon. The latter can be fixed with one flag:

+Weapon.Ammo_Optional

This flag actually has a number of effects, of which we're really only after one: the weapon will not switch away if AmmoType1 is empty. It also allows you to keep selecting the weapon even if both AmmoType1 and AmmoType2 are empty, which depending on what you're doing with it, may not be desirable, in which case you'd also add the flag +Weapon.Ammo_CheckBoth (see this ZDoom Wiki page for more information), which will only allow you to select the weapon if at least one of its ammo pools has ammo in it. Granted, that doesn't completely fix it - it makes the weapon instead able to keep firing even when empty. So where's our middle ground? Easy: A_JumpIfNoAmmo.

Fire:
  PISG A 0 A_JumpIfNoAmmo("Reload")
  PISG A 4
  PISG B 6 A_FireBullets(4,4,-1,6,"BulletPuff",1)
  PISG C 4
  PISG B 5 A_ReFire
  Goto Ready

There's still a huge hole here, in that even if you have no extra clips, you can still reload the gun. That's because we're not doing anything to check if the player has any left. So we'll fix both that and the problem where you can reload with a full clip by doing some essential checking in the Reload state before letting anything happen to the gun at all.

Reload:
  PISG A 0 A_JumpIfInventory("NewPistolLoaded", 12, 2) // If the gun's full, jump 2 states.
  PISG A 0 A_JumpIfInventory("9mmAmmo", 1, "ReloadWork") // If there's extra ammo, reload.
  PISG A 1 // Here's where that first line ends up - the gun does nothing and returns to Ready.
  Goto Ready
 ReloadWork:
  PISG A 1 Offset(0,35) A_PlayWeaponSound("weapons/shotgr") // click-clack.
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,82)
  TNT1 A 0 A_GiveInventory("NewPistolLoaded", 12)
  TNT1 A 8 A_TakeInventory("9mmAmmo", 1) // We'll treat one 9mmAmmo as one full clip.
  PISG A 1 Offset(0,82)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,35)
  PISG A 1 Offset(0,32)
  Goto Ready

So here we go; this weapon will not fire if it's empty, can be reloaded mid-clip, and will not reload if there is no reserve ammo. But some players may complain about you being stingy with ammo (never mind the fact that, since we set 9mmAmmo to have a max of 150, you could carry 150 clips!) if you let them waste half-finished clips, so let's figure out how to do things bullet by bullet. This is a bit more complicated, but it only requires a little magic in the Reload state.

Reload:
  PISG A 0 A_JumpIfInventory("NewPistolLoaded", 12, 2)
  PISG A 0 A_JumpIfInventory("9mmAmmo", 1, "ReloadWork")
  PISG A 1 // Remember, this is where we end up if both of the previous checks fail.
  Goto Ready
 ReloadWork:
  PISG A 1 Offset(0,35) A_PlayWeaponSound("weapons/shotgr")
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,82)
 ReloadLoop: // Here's where the magic happens!
  TNT1 A 0 A_TakeInventory("9mmAmmo", 1)
  TNT1 A 0 A_GiveInventory("NewPistolLoaded", 1) // Only give ONE bullet at a time)
  TNT1 A 0 A_JumpIfInventory("NewPistolLoaded", 12, "ReloadFinish") // If it's full, finish up.
  TNT1 A 0 A_JumpIfInventory("9mmAmmo", 1, "ReloadLoop") // If it's NOT full, keep it rolling.
  Goto ReloadFinish // And if it's not full but there's no reserve ammo, finish up anyway.
 ReloadFinish:
  TNT1 A 8
  PISG A 1 Offset(0,82)
  PISG A 1 Offset(0,72)
  PISG A 1 Offset(0,62)
  PISG A 1 Offset(0,52)
  PISG A 1 Offset(0,44)
  PISG A 1 Offset(0,38)
  PISG A 1 Offset(0,35)
  PISG A 1 Offset(0,32)
  Goto Ready

With that little change, your weapon will now reload individual bullets instead of entire clips at once - that loop is where all the magic happens. In short, it takes 1 bullet from reserve, gives 1 bullet to the gun, checks if the gun is full, and if it's not full, checks if there's ammo still in reserve to continue the cycle. The cycle ends if the gun is completely filled, or if there is not enough reserve ammo to finish filling the gun. If you decide you want the gun to have a different ammo capacity, you will need to change not only the Inventory.MaxAmount in the ammo type actor, but also the checks in Reload and ReloadLoop.

Save and test - your weapon ought to work just fine. Unless you're using Zandronum, in which case let's keep going!

As I stated at the beginning of this tutorial, Zandronum is (at the time of this writing) not nearly up to date with ZDoom, so WRF_ALLOWRELOAD (the magic flag that makes the Reload key work) currently produces an error on startup if this weapon is loaded in Zandronum. How'd we get by before ZDoom r3530? Read on, and I'll show you.

The reload "key" is created through the use of two CustomInventory items (one for the key's "on" state and one for the "off" state) and one regular Inventory item that keeps track of the state. Here's the code for them:

ACTOR Action_Reload : CustomInventory
{
    Inventory.Amount 1
    Inventory.MaxAmount 1
    -INVBAR
    States
    {
    Use:
        TNT1 A 0 A_GiveInventory("IsReloading",1)
        Fail
    }
}

ACTOR Action_ReloadCancel : CustomInventory
{
    Inventory.Amount 1
    Inventory.MaxAmount 1
    -INVBAR
    States
    {
    Use:
        TNT1 A 0 A_TakeInventory("IsReloading",1)
        Fail
    }
}

ACTOR IsReloading : Inventory
{
    Inventory.Amount 1
    Inventory.MaxAmount 1
    -INVBAR
}

A CustomInventory item is an item that stays in the player's inventory and will execute the code in the Use state when it's used from the inventory. We are deliberately hiding this item from ZDoom's built-in inventory bar, because we're going to use it directly with a keyboard key. Add this to a KEYCONF lump in your file:

addkeysection "My Tutorial Pistol" tutorpistol
addmenukey "Reload" +tutor_reload
alias +tutor_reload "use Action_Reload"
alias -tutor_reload "use Action_ReloadCancel"
defaultbind r +tutor_reload

The text in quotes for Addkeysection actually creates a named section in Customize Controls labeled "My Tutorial Pistol". Make sure that the text afterwards - "tutorpistol" in this case - is unique for every mod you create, because your INI file will store a section in it with that name. Name them uniquely every time unless you want configuration conflicts, like keys not being bound correctly. You can also rename the console action ("+tutor_reload"), but make sure to keep that name consistent when changing the defaultbind and alias lines.

In order to use this reload "key" to reload your weapon, you will need to add the appropriate line to your weapon's Ready state:

Ready:
  PISG A 0 A_JumpIfInventory("IsReloading", 1, "Reload")
  PISG A 1 A_WeaponReady
  Loop

Pay particular attention that the A_JumpIfInventory line has a duration of 0, or else the weapon may be unresponsive at times when the trigger is pulled. Also be careful with the initial check in the Reload state itself; if there is not at least 1 tic before the weapon returns to the Ready state, the game may freeze if you try to reload with a full clip.

You can actually use the Action_Reload trick to create more weapon functions if needed, for example you might want a Call of Duty-style melee button, or a button to throw grenades, or a button to change the current weapon's rate of fire. In that case, just create new CustomInventory items and a new Inventory to keep track of their state.

I do hope this tutorial has taught you a lot about how weapon reloading works in a game. If there are further questions, I'll be happy to clarify the tutorial a bit.

Diaz - Retrospective

I think it was in late 2005 that Marty Kirra and I were in frequent contact. We tended to bounce ideas off each other for our respective projects. I'd send him beta versions of Mister Cola and The Stranger to test, and he'd ask me all manner of gun-nut questions and stuff about his little universe he was making. That universe was the framework surrounding a character of his by the name of Agent Hernandez (he was never given a first name, and Marty and I both joked that he'd forgotten it). Marty eventually revealed to me that, once he learned a bit more about how ZDoom modding worked, he'd make Agent Hernandez his own mod. He mentioned to me another character of his, Olivia Diaz, that Hernandez was supposed to be sort of a "fanboy" of. I can't remember when I had the idea to give her a mod as well, but the original plan was that the Diaz mod would serve as a "prequel" to Hernandez. Well, so far, that hasn't happened yet...but here we go on a retrospective anyway.

After just under a year of work, I put out the original WW-Diaz.wad to /idgames. This was sort of a turning point for my own ZDoom work, where I stopped doing "experimental" stuff and paid more attention to making a more cohesive set of weapons. The one thing I planned on doing right from the outset, though, was getting away from the Doomguy hands. This was something I'd been wanting to do since 2004, back when I was working on the (never finished) Agent Zero mod for EDGE (cancelled because I never did figure out what I wanted to do with it, and fellow project member Chronoteeth was adding stuff to it that I really didn't want...really, cancellation was an act of mercy for that thing, neither of us knew what the hell we were doing back then).

The decision to create a "new" set of hands for Diaz had three major effects. Firstly, I (inadvertently) started a sort of trend among weapon modders to try their hands at using different sets of hands (like Brutal Doom's fingerless black gloves). Second, I find that newbie modders are far less likely to rip material from Diaz wholesale, at least, not without at least trying to edit different hands onto the weapons.

The third and most important effect of this decision...was that Marty and I had to Frankensprite a new hand for nearly every frame of animation. This really didn't do us any favors as far as getting the thing done in a reasonable amount of time. I still have no idea how it only took me "several months" to finish this mod, as opposed to the likely over a year it took to make the Hits Collection version. (Oh, did I not mention, this is only about the original release, and not the 2008 update?)

Really though, I had a lot of fun making this mod - possibly too much, as I'd later find that all my weapons were almost hilariously overpowered to "make up" for the reloading system. For this mod, I borrowed a reload system from DoomRater that enabled the use of a separate, freely-rebindable key to reload (as opposed to the Altfire-based system I used in The Stranger). However, instead of counting by bullets, this mod counted by entire magazines, discarding partially used magazines if the player reloads in the middle of one. A perhaps ill-advised decision I made, though, is that the game will refuse to let you reload unless you've used more than 50% of the ammo in the current magazine. Obsessive reloaders complained as well.

Another really bizarre decision I made, out of a complete lack of understanding of shotgun shells, was that instead of the generic red buckshot shells, I'd make the two shotguns fire triple-B birdshot. For those that don't understand what I'm going on about, birdshot shells are not loaded with the larger lead balls that buckshot shells get. Birdshot is instead loaded with around a hundred very small balls, which - if the name is any indication - is primarily intended for hunting birds, due to the very small size and very high number of them. So my shotguns - the Makoto Type 301 and the SPAS-12 (with some of the positively ugliest model rip sprites I've allowed into any of my mods since Weasel's Weapons) - instead of only firing a few pellets per shot with an average damage, fired almost 100 hitscan pellets dealing only 1 or 2 hitpoints of damage apiece. This made the shotguns almost hilariously overpowered, but also brought many lesser CPUs to a grinding halt due to the particle effect bullet puffs I borrowed from Caligari87. Nowadays, you're less likely to see any performance issues there, but the Hits Collection version still opted to reduce this back to buckshot levels just because of how silly the whole thing was.

If WW-Diaz had any major failing though, it's the "super" weapon - the BFG replacement, the railgun-like Arc Welder, is actually pretty underwhelming as a weapon, since it takes several shots to even take down a Baron. That's why I replaced it with the Neutralizer in the update (which is still kind of lame, but significantly less so, and also more widescreen-friendly). The real star of the show, as far as heavy power goes, is the RPG-7, which is a pretty awesome crowd-clearing weapon. The RPD (which is actually supposed to be an RPK-74, but I didn't know that at the time) is also pretty overpowered, but its uses are heavily limited, thanks to the requirement that you set the weapon up on its bipod before you can use it at all (which I lifted for the update).

The most important lesson I learned from making Diaz was that, just because I've balanced my mod to where I can play it comfortably, doesn't mean that it's balanced for your average Doomer. In the interests of full disclosure, I actually suck a lot at Doom, and tend to play on Hurt Me Plenty on everything. Hell Revealed presents difficulty issues for me. For just about every mod I made after this one, I decided that I should start balancing it to where I would start having trouble with the game again, but not in such a way that the game is unfair. There's a number of ways I've found to soup it up like this, like the projectile bullets in WW-Nazis. But the point is, after this mod, I quit making my weapons so powerful and brought skill back into things.

Some time after WWHC-Diaz was released, I was informed that Richard Smith Long actually made an add-on patch for the original Diaz. As it turns out, some crazy fellows actually prefer this version over the update, but still wanted the recoil and other effects from the Hits version...I really don't understand why that got made, to be honest.

Get Diaz (original version) from /idgames archives

TUTORIAL: How to Make Your Weapons Fun to Use

This tutorial comes with two caveats:

* I assume you're already familiar with the basics of creating your weapon, from the graphics to importing them into your WAD file, to coding it up in Decorate and aligning everything.
* I'm going to be mentioning a lot of abstract concepts (many of which I may or may not have just made up on the spot). Bear with me. You might understand what I'm talking about.

Alright. So you've got yourself a kick-ass new shotgun for your mod, it's all graphic'd up and you've got a sweet firing sound and everything. But you get in game and find that you just don't have much fun using the weapon as you did with some other guy's shotgun from another mod. A few people might think that the solution is to just toss the shotgun they made and rip the other guy's instead, but this tutorial's purpose is to show that you don't need to give up. Here's a few steps that I tend to take if my guns aren't fun to use.

Step 1: Check the concept.

In a lot of cases this won't be a problem, but if you're creating a weapon that hasn't been made before (like a swarming plasma gun or something), maybe the very idea of the weapon needs work - perhaps your gun sounded cooler on paper than it does in practice, maybe some special weapon effect you planned just doesn't work very well in the engine (like a resurrect gun or magic ricochet bullets). But like I said before, in a lot of cases this won't be a problem, especially if all you're making is a shotgun.

Step 2: Weapon Power to Sound Power ratio.

A little concept I figured out while playing STALKER: one of the things that makes a gun fun to use is the weapon's power compared to the perceived power of the sound it uses when it fires. Say you've got yourself a sweet new magnum, capable of killing an Imp with a single shot, a Demon in two shots, and so on. The sound you've chosen...well, it sounds like a pack of Fun Snaps or some guy hitting a wall with a hammer. That's just not going to work. If you want your weapon to feel appropriately powerful, you want your gunfire to have the right volume (gunfire tends to be LOUD, louder than most other parts of the game), appropriate reverb (Doom's primarily an indoor game so let's not go overboard on the outdoorsy echo), and of course the right amount of punch (hint: gunshots don't tend to "fade in"). It's a tricky concept to work with, but as long as the "power" of your weapon sound matches closely enough with the power of the actual gun, you'll be fine. (Let's also make sure this doesn't go the other way around: a 9mm peashooter with the sound of a nuclear bomb? Uh...)

Step 3: Add some "weight" to your weapon animations.

People aren't computers. When we move our limbs about, we tend to do so with a small degree of inertia; the way the universe works will tend to make it difficult for us to move at a constant speed with no inertia whatsoever. So when I animate a weapon's reload sequence, I tend to work with a sort of inertia: longer, drawn-out movements will start out slower near the beginning of the movement (say a hand coming up to the shotgun to load a shell into it), speed up a little in the middle, then slow down near the end (as the hand will need to slow down in order to not miss the insertion port). Here, have a (cheesy) graph:

1__2____3_________4______________5__________6_____7__8

The numbers indicate the position of the hand on screen for that frame (assuming that the hand will take 8 frame to reach the shotgun, and of course assuming that the shotgun is on the right side of the screen...no discrimination against you lefties). If every frame of that sequence has the exact same duration in tics, the perceived speed of the hand's movement will still appear to change, and appear less robotic and artificial because it's not moving at a constant speed. Get the picture? (I hope so, because I can't figure out how to explain it further.)

This same technique may also come in handy for other animation sequences, like on-screen weapon recoil.

Step 4: Quality of resources.

Of course all of the above might be a moot point if your sprite looks terrible. Let's face it: not everyone is an artist, and I don't expect everyone to be, but when your mod is comprised mainly of Doom-style weapons and the weapon you've just added looks like an MS Paint sketch, that's going to be pretty distracting and make your weapon less fun to use (see my own Doomam.wad and its terrible mass driver weapon). Sometimes, you just need to toss your resources and get/make new ones.

I hope these little guidelines will help someone.

TUTORIAL: Making Your Project Less of a Pain to Download and Run

There are some WADs out there that are just plain unfriendly. Sometimes there are console variables that need to be set, sometimes it'll only work in a specific game mode, sometimes the WAD is split into about fifty different files to cover for every single permutation of settings. This guide should help you make your wad somewhat less of a pain for the user to run.

1. Instructions!
Not everybody's going to know how to play with your mod. Heck, one person once complained to me that ammo limits were too strict in WW-Diaz (turns out, they had not set a Reload key and thought that the player literally only started with 12 pistol rounds!). The best thing you can do is include an instruction manual with your mod. Not a huge, fifty-page tome that describes the backstory and every little bit of lore - a simple, easy-to-reference instruction sheet that tells the user precisely, in as clear and simple terms as possible, what you need to do to get the game started and play with the game.

There are some mods out there, though, that really need the aforementioned tomes (All-Out War 2, for example). That's because the mods really are complex enough to need multiple pages of instructions. For your average gun replacement mod, though, all you should really need is a quick guide on what each weapon does and possibly a note that tells players to make sure to bind a reload key.

2. Clear up your filenames!
If they're anything like me, most Doom players will just have a folder full of WAD files. If your project comes with more than one WAD file - or even more than one readme file - make sure it's named similarly to the WAD file so that it doesn't get lost! The worst thing you can do to a player that just dumps files into one folder is to name your files something generic, like "doomtk.wad" or "readme.txt". Unless you plan on running your WAD file in DOS with vanilla Doom (or uploading your WAD to /idgames...Lord only knows why they still need you to do this), you don't necessarily need to limit your filenames to the DOS-standard 8.3 filename formats. Not to say that all WAD files should be called "HURDY-SPLURBY BORK BORK BORK.wad" - just whatever clears up the wad's identity at a quick glance.

3. Don't waste space!
In the majority of cases, WAD files don't tend to be very large, but I'm seeing some people complain about how some files are just too large. Well, you'd hate to throw out all that work to keep sizes down, so here's some tips on saving space...

Compression! Not just sticking your WAD file inside of a ZIP - look over the insides of your WAD file. Skulltag supports graphics compressed in PNG format (lossless, meaning the graphics will not be degraded in quality) and sounds compressed in Ogg Vorbis (in most cases, using the basic quality settings on OggEnc will result in a sound that is one-quarter the size of the original wave file with no loss of sound quality). With mods that have a lot of such files in them, this can really help keep sizes more manageable. (The current version of Agent Hernandez: Dawn of Tomorrow holds over 4,000 lumps worth of data and comes to just under 18 MB in its WAD form. If uncompressed, I'd imagine it being somewhere closer to 45 MB.)

Trim useless things! I'm amazed that this tends to be the case at times, but I've seen a few WAD files that have several sprites or sounds that all have the same name - indicating, in most cases, that a lump was "replaced" at some point during development. Delete the old files if you're going to put in replacements.

Re-assess the needs of the WAD file. If all you're making is a simple gun replacement, then you really don't need that 12-minute-long metal epic as the title screen music. Indeed, unnecessary MP3/OGG music tends to be the main contributor to most overbloated WAD files.

4. Tell me what things are!
If you really need your mod to include several WAD files, mention in the instruction manual (you did read point #1 about that, didn't you?) which WAD files do what, and preferably which order to load them in. So if you have the smoke and shell casing effects in another wad file, don't just leave your mod as "MyMod1.wad" and "MyMod2.wad" - either name them properly (point #2) or say which WADs are optional in the instruction card (point #1).

5. Don't make the user dive into the WAD file to figure out which map they're supposed to start on!
This one's a particularly sore subject with me. WADs occasionally do not include instructions or helpful MAPINFO lumps that tell me which map to start on. Some mods tend to name their maps in some really crazy conventions (All-Out War 2, ZDoom Wars, among many, many others) and I end up having to dig through the WAD file to figure out what all the map names are. Don't put your players through this. Once again, instructions!

6. ZIP woes.
Okay, so Skulltag supports loading compressed ZIPs instead of WADs now. When you're distributing such a mod, don't just upload the ZIP file! A lot of people tend to be confused when they open the ZIP file and see a bunch of Sounds, Graphics, Sprites, Music, and Actors folders. If you choose to use ZIP format for your mod, rename it to something that people are less likely to mistake for a ZIP file (PK3 seems to be a popular choice but anything works), then compress THAT into a ZIP file. Okay, so redundant compression will actually increase the file size slightly, but it's all in the name of user-friendliness, plus you can also include the necessary instructions alongside the PK3 instead of burying it in the PK3 itself!

That should be it for this list. Basically, don't assume that your player knows as much about the mod as you do. Throw 'em a bone at some point.

TUTORIAL: Adding Your Own Sound Effects

I admit, I've made a grave omission in my last several tutorials - most of the time, we've just been borrowing the default Doom sound effects for our tutorial weapons. In this tutorial, I'll show you how to add your own. You'll probably want a sound editor like Audacity, though I won't be covering the actual creation of sound effects here, only how to get them working in your mod.

Not everybody has the skills to record and mix their own new weapon sounds. Personally, I remedy this with FreeSound, a site where you can obtain your own Creative Commons-licensed sound effects for any usage. With very few exceptions, all of these sounds are free for your use and modification, so have at 'em.

Once you have your sound effect (hopefully in a format that ZDoom supports), fire up SLADE and get a PK3 going (or open a previous one). For PK3 files, all sound effects need to be in a subfolder called "sounds" (sans quotes, obviously). You're free to use any kind of branching folder structure you like, for example, you could store all sounds relating to your shotgun in /sounds/shotgun, or all sounds relating to your new zombie in /sounds/monsters/zombie2 - the thing is, though, since ZDoom actually ignores most of that, try not to duplicate file names, and don't name your files any longer than 8 characters. Just a quick breakdown here:

/sounds/DSPISTOL - This is fine. However, in a PK3, you might want to leave the file extension on.
/sounds/DSPISTOL.ogg - This is also fine. However, it helps organization a lot if you have your things organized.
/sounds/pistol/DSPISTOL.ogg - This is ideal - keep your weapon sounds separate from everything else. I generally keep an individual folder for each weapon, a folder for pickup sounds, folders for every enemy, and a folder for just miscellaneous stuff like door sounds.
/sounds/pistol/PistolFire.ogg - This will not do, since ZDoom will only be able to see the first eight characters, i.e. it'll only be recognized if you're specifically searching for PISTOLFI.

Okay. At this point, I'll assume that you've got your sounds imported and organized where they ought to be. Sounds won't usually work unless you've defined them in SNDINFO. SNDINFO is actually a pretty simple "language" - at a very basic level, you really only need one line per sound. A basic SNDINFO definition consists of a "nice name" and a "logical name" - the "nice name" is what you will be referencing from Decorate. The "logical name" is the name of the sound as it's stored in your archive. Here's a sample of how we would define our above-mentioned pistol sound in SNDINFO.

weapons/PistolFire DSPISTOL

Granted, we don't necessarily need to define a sound named DSPISTOL (since ZDoom.pk3 has already taken care of that for us, as DSPISTOL is in Doom.wad already), but this just demonstrates the proper form. In your Decorate code, you'd reference the nice name ("weapons/pistolfire") either in the weapon's AttackSound, or through an A_PlaySound function.

There are some other things you can do with SNDINFO, such as making a bank of random sounds for ZDoom to choose. Here's an example of that:

$random Weapons/PulseFire { Weapons/PulseFire1 Weapons/PulseFire2 Weapons/PulseFire3 Weapons/PulseFire4 }
Weapons/PulseFire1 PULSE1
Weapons/PulseFire2 PULSE2
Weapons/PulseFire3 PULSE3
Weapons/PulseFire4 PULSE4


A $random statement first begins with the name you eventually want to reference in your code. Afterwards, there is an opening brace (or curly-bracket or whatever you want to call that), then from there you list the names of all the sounds that you want ZDoom to randomly choose between when the sound is called. Remember to close your brace when you're done. Then make sure the randomized sounds are also defined (since we can't make a $random statement reference file names, only nice names), and call the name you defined from $random whenever you need it.

There are many other things you can do with SNDINFO and Decorate, but I encourage you to read the Wiki entries I linked earlier in the tutorial and experiment with them yourself.

TUTORIAL: Inventory "Variables" and Making a Sniper Rifle

It's high time we learned how to use what is arguably one of the most important things to know when making a reasonably complicated Decorate weapon: the use of dummy inventory items as a "variable" system. If you're not a programmer by trade, a variable is basically the term to describe a number that is stored in memory to be referred to at a given point elsewhere in the program.

The simplest use of a variable is as a Boolean (we talked about those in the very first tutorial), a number that is only 0 or 1. And what we'll do here is use this knowledge to create a simple zooming sniper rifle by checking the status of our variable and using A_JumpIfInventory to make the weapon behave a certain way.

First, let's define our dummy item.
ACTOR SniperZoomed : Inventory
{
Inventory.MaxAmount 1
}

If you want there to be multiple states of zoom, you'll probably want to increase the MaxAmount to something above 1, but for purposes of this tutorial, we only have two states: Zoomed (1) and Unzoomed (0). We'll refer back to SniperZoomed in our weapon, which I'll just be constructing out of the basic Doom shotgun graphics. (Incidentally, Inventory.MaxAmount 1 is defined by default as part of the Inventory class, so you technically don't need to put it in here if you're only going to have two states. You will need to define a different maximum, though, if you want to have larger values.)

Go ahead and set up your weapon as usual - make it take Shells as an ammo type, and assign it the usual Select and Deselect states. The Ready state is where things will get a bit different, so don't write one up just yet.

Our sniper rifle will have two different Ready states. The first ("real") Ready state will simply display the shotgun in its usual position, but will also include an A_JumpIfInventory checking for the presence of the SniperZoomed inventory item, and if it is present, will jump to a special state we'll call ReadyZoomed. This is what is known as a custom state label; you can name it pretty much anything you want (as long as it's not reserved by ZDoom for a special function, like Flash), and refer to it using any of the various Jump or Goto commands.

Go ahead and write up a Ready state for your new sniper weapon, and on a new zero-duration state immediately before your A_WeaponReady line, add a line that checks for SniperZoomed and jumps to ReadyZoomed. Then, make a ReadyZoomed label, with only an A_WeaponReady line using the sprite TNT1A0 (the "blank" sprite I mentioned a couple tutorials ago), and end it with Goto Ready. Here's one I made earlier:
Ready:
SHTG A 0 A_JumpIfInventory("SniperZoomed",1,"ReadyZoomed")
SHTG A 1 A_WeaponReady
Loop
ReadyZoomed:
TNT1 A 1 A_WeaponReady
Goto Ready


We haven't really done anything with the actual zooming yet, but don't worry, we'll get to that in the AltFire state. Our altfire will be pretty straightforward. When the altfire key is pressed, the weapon checks if SniperZoomed already exists. If it doesn't, the weapon gives that item through A_GiveInventory, sets A_ZoomFactor to an appropriate level, and then goes back to the Ready state. If the earlier inventory check shows that SniperZoomed is already in the inventory, it jumps instead to an UnZoom state which removes the SniperZoomed item from the inventory, resets A_ZoomFactor to a normal level, and then returns to the Ready state. If you've been paying attention, it shouldn't be much problem for you to write this state yourself, but in case you're having problems, here's one I baked earlier.
Altfire:
SHTG A 0 A_JumpIfInventory("SniperZoomed",1,"UnZoom")
SHTG A 0 A_GiveInventory("SniperZoomed",1)
SHTG A 0 A_ZoomFactor(3)
Goto Ready
UnZoom:
TNT1 A 0 A_TakeInventory("SniperZoomed",1)
TNT1 A 0 A_ZoomFactor(1)
Goto Ready

A_ZoomFactor is a recent addition to ZDoom. The argument it takes is what the player's FOV (field of view) will be divided by while the zoom is in effect. So an A_ZoomFactor of 3 will divide the default FOV (in this case probably 90) by 3, resulting in an FOV of 30 degrees and a view that is zoomed approximately 3 times normal. If that's confusing, just remember that a higher A_ZoomFactor will result in a more zoomed-in view, and an A_ZoomFactor of 1 resets the view back to normal.

If you were to save and test this right now, there would be some issues. For starters, while the weapon is no longer in view while the zoom is in effect, it suddenly reappears when you fire, and if you try to switch away from the weapon while zoomed, the game "remembers" the zoom and immediately jumps back to it when you switch back to the sniper rifle. So we'll need to do two things here: first, we'll need to make the Fire state check the value of SniperZoomed as well, so we can create a substitute fire state that does not display the weapon graphics. Second, we'll add some extra code to the Deselect state that removes the zoom and inventory item, so that the zoom does not "stick."

Let's take care of this blatant error with the fire state first. Do the usual A_JumpIfInventory at the very beginning of the fire state, and have it jump to a FireZoomed state that is identical to the normal Fire state except that all the sprites are TNT1A0. (You could also make it display a sniper scope graphic instead, but we'll ignore that for now.) Probably would look a bit like this:
Fire:
SHTG A 0 A_JumpIfInventory("SniperZoomed",1,"FireZoomed")
SHTG A 2 BRIGHT A_FireBullets(0,0,1,30,"BulletPuff",1)
SHTG BCDCB 5
Goto Ready
FireZoomed:
TNT1 A 27 A_FireBullets(0,0,1,50,"BulletPuff",1)
Goto Ready

You might notice that I made the FireZoomed state cause more bullet damage than the unzoomed version. This is a little gameplay balancing trick - to cause the most damage with this sniper rifle, the player would need to aim carefully using the Zoom function and restrict their view of the world around them. It's what the gaming business would call "risk vs reward" - if you take greater risks by giving yourself a penalty (like your field of view being narrow and being unable to see things sneaking up on you), you stand a chance of getting a greater reward (like doing more damage to your target and potentially not having to spend as much ammo taking him down).

As for the Deselect fix, it's as simple as adding a two zero-duration lines to its beginning:
Deselect:
TNT1 A 0 A_TakeInventory("SniperZoomed",1)
TNT1 A 0 A_ZoomFactor(1)
SHTG A 1 A_Lower
Loop

Don't worry about the first two commands repeating when looped; it won't have any effect after they've been executed once. Save and test your weapon with these changes and you'll have a neat zooming sniper-shotgun that obeys the laws of physics.

(Another word from Ed the Bat: In this case, you could actually end your Deselect state with Wait instead of Loop; this tells ZDoom to loop only the last state. If your Deselect state is particularly complex, this could potentially save some strain on the processor.)

Inventory variables like the SniperZoomed flag we used in this tutorial can come in very, very handy. There's tons of things you can do with them, from weapons that reload to weapons that can charge, weapons that can overheat, or even a melee weapon with a combination attack. The use of inventory variables is arguably the most powerful thing available to weapon makers today.