Custom CVARs with ZScript

From ZDoom Wiki
Jump to navigation Jump to search

This will show you how to control behaviour of actors in ZScript using custom CVARs. This way you can easily add options to your level or mod.

Server CVars

Server CVars are console variables defined with the server keyword. They're globally available and readable, and their value is the same for all players in a multiplayer game. Any CVar that affects gameplay MUST be a server cvar.

For this example, we'll make it so that Zombiemen explode when they become active, but only if a custom CVAR is set to true.

Custom variable

The first thing that is needed is a CVARINFO lump that defines the CVARs that we're going to use. With only one CVAR, the file will only be one line:

server bool explodey_zombies = true;

Actor code

Note: Server CVars can be read in ZScript directly. They do NOT require any special methods or functions; they're always available by their name, like global variables.


There are many ways to implement exploding Zombiemen. Here is one possibility.

class ZombiemanBoom : Zombieman replaces Zombieman
{
    States
    {
    See:
        TNT1 A 0 A_JumpIf(explodey_zombies == true, "Explode");
        goto Super::See;
    Pain:
        TNT1 A 0 A_JumpIf(explodey_zombies == true, "Explode");
        goto Super::Pain;
    Death:
        TNT1 A 0 A_JumpIf(explodey_zombies == true, "Super::Explode");
        goto Super::Death;
    Explode:
        TNT1 A 0 
        {
            A_Die;
            A_Explode;
        }
        MISL BCD 5 bright; // Uses the Rocket sprites
        stop;
    }
}

In the replacement Zombieman, we preempt the See and Pain states to check if our custom CVar is true. If the CVAR is false, the Zombieman will behave as normal.

Now Zombiemen will explode on sight when you start up a map, but you can stop this by typing explodey_zombies 0 in the console. To set this value to the default, you must type archive explodey_zombies after setting the CVAR to 0.

User and nosave CVars

While server CVars are global and have the same value for all players, user and nosave CVars are local and each player can have their own value for this CVar. These two types can be used for some purely visual features, such as modifying the player's UI or anything else that does NOT affect playsim. This like modifying gameplay and actors, spawning things, must NOT rely on these types of CVars.

The difference between user and nosave is explained on the CVARINFO page, but primarily the idea is that user CVars can still be shared between players (so, for example, a player's nickname is a user CVar), while nosave CVars are not shared (so options like what type of HUD you're using would be a nosave CVar).

In the example below we'll use a nosave CVar to allow the player to alter the sprites of their weapon. We're using nosave here because we assume this data is not relevant to other players, and this is a visual-only option.

The weapon assumes to have 3 different sprite sets, all with the same number of frames in the same order, for example:

  • Set 1: WEA1 ABCDEF
  • Set 2: WEA2 ABCDEF
  • Set 3: WEA3 ABCDEF

To declare a nosave CVar, define it with the nosave keyword:

nosave int weaponstyle = 0

User and nosave CVars cannot be read directly, in contrast to server CVars. For user CVars we need CVar.GetCVar. For nosave CVars you can use CVar.FindCVar:

class MyWeapon : Weapon
{
	action void A_SetWeaponStyle()
	{
		// Get a pointer to the desired CVar:
		CVar c = CVar.FindCVar('weaponstyle');
		// If for some reason we couldn't get the pointer,
		// do nothing else:
		if (!c)
		{
			return;
		}
		// Get a pointer to the sprite layer where
		// this function was called:
		PSprite psp = player.FindPSprite(OverlayID());
		// Null-check it for safety (although it should
		// never be null in this case):
		if (!psp)
		{
			return;
		}
		// Modify the sprite of the calling sprite
		// layer based on the value of the CVar.
		// Notice that we're not modifying the frame
		// letter, only the sprite name:
		switch (c.GetInt())
		{
		default:
			psp.sprite = 'WEA1';
			break;
		case 1:
			psp.sprite = 'WEA2';
			break;
		case 2:
			psp.sprite = 'WEA3';
			break;
		}
	}

    States
    {
    Select:
        WEA1 A 0 A_SetWeaponStyle(); // set the sprite
        #### A 1 A_Raise; // use #### for all subsequent sprites so we don't override it
        wait; // use wait so we don't 
    Ready:
        WEA1 A 0 A_SetWeaponStyle(); // set the sprite
        #### A 1 A_WeaponReady; // use #### for all subsequent sprites so we don't override it
        wait;
    Fire:
        WEA1 A 0 A_SetWeaponStyle(); // set the sprite
        #### B 5
        {
            A_FireBullets(5, 3.5, 1, 5);
            A_GunFlash();
            A_StartSound("mygun/fire", CHAN_WEAPON);
        }
        #### CDE 4;
        #### F 5 A_ReFire;
        goto Ready;

        ... // The rest of the code omitted to keep this example short
    }
}

See also:

Creating options for your CVars

Having done anything of the above, you will add a CVar but it will only be modifiable through the console. To add options visible through the menu, you'd normally create a custom menu for your project, and options to it through MENUDEF. For example:

Note: This code is an example. In practice authors are strongly encouraged to NOT add player-facing text strings into code directly, and instead use the LANGUAGE lump.
// This creates a new custom options menu:
OptionMenu "MyModOptions"
{
  // Add an option for the explodey_zombies CVar:
  Option "Exploding Zombiemen", "explodey_zombies", "OnOff" //OnOff is an existing optionvalue, you don't need to define it
  // Add n option for the weaponstyle CVar:
  Option "Weapon style", "weaponstyle", "WeaponStyleOptions" //WeaponStyleOptions will be a custom option value
}

// A custom option value that attaches unique names
// to the possible CVar values:
OptionValue "WeaponStyleOptions"
{
  0, "Classic"
  1, "Enhanced"
  2, "Special"
}

// This will make your menu appear in the default
// GZDoom options menu, right before the CustomizeControls
// section:
AddOptionMenu "OptionsMenu" before "CustomizeControls"
{
  Submenu "My mod options", "MyModOptions"
}

// This does the same but also will make it visible
// if the player has Simple Options Menu enabled:
AddOptionMenu "OptionsMenuSimple" before "CustomizeControls"
{
  Submenu "My mod options", "MyModOptions"
}

See also