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
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)
FireHalftic:
CHGG A 0 A_TakeInventory("HalfTicCounter", 1)
CHGG B 3 A_FireCGun
```Fire: