header general

Alternate Fire/Reload/Zoom/...States on Weapons

Thanks to WildWeasel for teaching me a non-ACS way of doing this. I've been meaning to write this for ages, but never got around to it.

One of the more obscure tricks with weapons is the ability to add a new firing state (a state that can be activated aside from primary and alternate fire). This is usually used to have a key that specifically zooms or reloads. However, this is actually quite a simple trick.

For reference, here are three wiki pages:


I'll put it in basic terms, then explain each part further. Essentially, you have a dummy inventory item as a flag for the weapon to check for in the Ready state, and this item is given and taken away by a pair of CustomInventory items which are bound to a key.

I'll use a third Fire state as an example. To explain further, we start with our fake inventory item; an item that literally has
no use besides A_JumpIfInventory to check for:


Actor IsFiring : Inventory
{
  Inventory.Amount 1 //This doesn't technically need to be here but it's better to make sure
  Inventory.MaxAmount 1
  -INVBAR //Keeps it from displaying in the inventory bar, but you don't have to worry
  States
  {
  Spawn:
    TNT1 A 1
    Fail //Keeps it from really doing anything in the world
  }

All this will do is sit in your inventory. Now we have an alternate pistol checking for this:

Actor Pistol2 : DoomWeapon 5010 //Named differently so it won't conflict with Doom's
  {
  Game Doom
  Weapon.SelectionOrder 1900
  Weapon.AmmoUse 1
  Weapon.AmmoGive 20
  Weapon.AmmoType "Clip"
  AttackSound "weapons/pistol"
  Obituary "$OB_MPPISTOL"
  +WEAPON.WIMPY_WEAPON
  Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED"
  Decal BulletChip
  States
  {
  Ready:
    PISG A 0 A_JumpIfInventory("IsFiring", 1, "AltAltFire") //We're checking for the fake inventory item
    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_FirePistol
    PISG C 4
    PISG B 5 A_ReFire
    Goto Ready
  AltFire: //Because why not, we're already using altfire!
    PISG A 4
    PISG B 6 A_FirePistol
    PISG C 4
    PISG B 5 A_ReFire
    Goto Ready
  AltAltFire:
    PISG A 4
    PISG BBB 0 A_FirePistol
    PISG B 6 A_FirePistol //Fires four times so we know it's working
    PISG C 4
    PISG B 5 A_ReFire
    Goto Ready
  Flash:
    PISF A 7 Bright A_Light1
    Goto LightDone
    PISF A 7 Bright A_Light0
    Goto LightDone
  Spawn:
    PIST A -1
    Stop
  }
}

There! Our pistol is checking for that item, and has a new firing state. Now we just need those two CustomInventory items:

Actor Action_Fire : CustomInventory
{
  Inventory.Amount 1
  Inventory.MaxAmount 1
  -INVBAR
  States
  {
  Use:
    TNT1 A 0 A_GiveInventory("IsFiring", 1)
    Fail // It's important that these items end in "Fail" instead of "Stop" or else they are removed from inventory as soon as they are used. Fail will keep them in your inventory.
  }
}

Actor Action_FireCancel : CustomInventory
{
  Inventory.Amount 1
  Inventory.MaxAmount 1
  -INVBAR
  States
  {
  Use:
    TNT1 A 0 A_TakeInventory("IsFiring", 1)
    Fail
  }
}


When Action_Fire is used, it will give the fake item, but when Action_FireCancel is used, it will take it away again. Thus, using one will make the pistol fire, and the other will make it stop. It is important that both of these are given to the player at the start with Player.StartItem, or this will not work.

Now to bind these two items to a key:

AddKeySection "Your Section Name" YourSectionsName
AddMenuKey "Alternate AltFire" +altaltfire
Alias +altaltfire "Use Action_Fire" // + events occur when the key is pressed.
Alias -altaltfire "Use Action_FireCancel" // - events occur when the key is released.
DefaultBind x +altaltfire // only the + event needs to be bound.


Check the wiki link above about adding keysections for explanations of what these do. Now, when the key is pressed it will give the item (activating AltAltFire), and when the key is released, it will take the item away (making it stop firing).

And it's that simple! If you want to test it out, here's a full Decorate and KeyConf:

Actor Pistol2 : DoomWeapon 5010 //Named differently so it won't conflict with Doom's
  {
  Game Doom
  Weapon.SelectionOrder 1900
  Weapon.AmmoUse 1
  Weapon.AmmoGive 20
  Weapon.AmmoType "Clip"
  AttackSound "weapons/pistol"
  Obituary "$OB_MPPISTOL"
  +WEAPON.WIMPY_WEAPON
  Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED"
  Decal BulletChip
  States
  {
  Ready:
    PISG A 0 A_JumpIfInventory("IsFiring", 1, "AltAltFire") //We're checking for the fake inventory item
    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_FirePistol
    PISG C 4
    PISG B 5 A_ReFire
    Goto Ready
  AltFire: //Because why not, we're already using altfire!
    PISG A 4
    PISG B 6 A_FirePistol
    PISG C 4
    PISG B 5 A_ReFire
    Goto Ready
  AltAltFire:
    PISG A 4
    PISG BBB 0 A_FirePistol
    PISG B 6 A_FirePistol //Fires four times so we know it's working
    PISG C 4
    PISG B 5 A_ReFire
    Goto Ready
  Flash:
    PISF A 7 Bright A_Light1
    Goto LightDone
    PISF A 7 Bright A_Light0
    Goto LightDone
  Spawn:
    PIST A -1
    Stop
  }
}

Actor IsFiring : Inventory
{
  Inventory.Amount 1 //This doesn't technically need to be here but it's better to make sure
  Inventory.MaxAmount 1
  -INVBAR //Keeps it from displaying in the inventory bar, but you don't have to worry
  States
  {
  Spawn:
    TNT1 A 1
    Fail //Keeps it from really doing anything in the world
  }
}

Actor Action_Fire : CustomInventory
{
  Inventory.Amount 1
  Inventory.MaxAmount 1
  -INVBAR
  States
  {
  Use:
    TNT1 A 0 A_GiveInventory("IsFiring", 1)
    Fail // It's important that these items end in "Fail" instead of "Stop" or else they are removed from inventory as soon as they are used. Fail will keep them in your inventory.
  }
}

Actor Action_FireCancel : CustomInventory
{
  Inventory.Amount 1
  Inventory.MaxAmount 1
  -INVBAR
  States
  {
  Use:
    TNT1 A 0 A_TakeInventory("IsFiring", 1)
    Fail
  }
}

Actor DoomPlayer2 : DoomPlayer
{
  Player.StartItem "Pistol2", 1
  Player.StartItem "Clip", 50
  Player.StartItem "Fist"
  Player.StartItem "Action_Fire", 1
  Player.StartItem "Action_FireCancel", 1
}


KeyConf:

ClearPlayerClasses
AddPlayerClass DoomPlayer2

AddKeySection "Your Section Name" YourSectionsName
AddMenuKey "Alternate AltFire" +altaltfire
Alias +altaltfire "Use Action_Fire" // + events occur when the key is pressed.
Alias -altaltfire "Use Action_FireCancel" // - events occur when the key is released.
DefaultBind x +altaltfire // only the + event needs to be bound.


Keep in mind, by the way, Fire and AltFire have some special behavior that alerts monsters, and A_Refire only works for Fire/Hold and AltFire/AltHold. Here's another AltAltFire that emulates that behavior:

AltAltFire:
    PISG A 4 A_AlertMonsters //Because it alerts monsters as soon as the state is entered.
    PISG BBB 0 A_FirePistol
    PISG B 6 A_FirePistol //Fires four times so we know it's working
    PISG C 4
    PISG B 5 A_JumpIfInventory("IsFiring", 1, "AltAltFire") //Fire again if the button is still pressed, like A_Refire.
    Goto Ready