ZScript_Basics

🟢 «< BACK TO START

🔵 « Previous: Classes instead of actors 🔵 » Next: Anonymous functions


How to see your classes in the game

So, you’ve created a class. How do you see it in the game?

The answer depends on the exact goal.

Spawning a class for testing purposes

If you just want to see your class in the game to test how it works, all you need to do is spawn it. For that you can use the summon console command. Open the console using the tilda (~) key and type:

summon <classname>

where classname is the actual name of your class. For example, summon zombieman will summon a Zombieman.

Note: console commands, just like ZScript itself, are case-insensitive.

Note, like any other console command, you can bind it to a key using the console. For example:

bind m "summon zombieman"

This will let you summon a Zombieman simply by pressing the M key. This command will be saved to your config, so you won’t have to re-enter it every time you load GZDoom. Doing something like this may be a good idea if you’re working on a complex class and need to constantly load the game and see how it looks, since typing the command every time is rather annoying.

If you’re creating a monster and would like to observe its behavior without being attacked by it, you can summon it as a friend using summonfriend console command:

summonfriend zombieman

This will summon a zombieman as a friend, as if it had a +FRIENDLY flag in its definition.

There are many other useful console commands you could use during testing; I recommend checking out the CCMDs page on the ZDoom wiki.

Replacing existing classes in a mod

If you’re creating a weapon/gameplay mod that does not include any maps, you can’t place your actors on the maps manually; instead you will have to replace existing classes, so that yours spawn instead. There are several ways to achieve that.

The most basic way, which has been available since DECORATE, is to use the replaces keyword in the actor’s definition:

// This actor inherits from Cacodemon and will also replace
// all cacodemons in the maps you play with this code:

class MyCustomCacodemon : Cacodemon replaces Cacodemon
{
    // actor code
}

For clarity, the syntax here is:

class <NewClass> : <ParentClass> replaces <ClassToReplace>

This should explain why Cacodemon comes twice in this example: the first time is the parent class to inherit from, while the second time points to which actors it should replace in the map when they spawn.

There are two downsides to this method:

  1. If your mod is run with another mod or a map that includes replacements for the same monster, they may conflict. In fact, the last replacement always takes precedence. So, for example, if you have a custom version of a Cacodemon in your mod, and your mod is played with a mapset that also comes with its own custom Cacodemon, the monster from the file that comes last in the load order will be used. For example, if you’re running GZDoom from command line or a .bat file:

    // With this order your cacodemon will be used:
    gzdoom.exe -file mapset.wad yourmod.pk3
       
    // And with this one you'll see the cacodemons from the mapset, not your mod:
    gzdoom.exe -file yourmod.pk3 mapset.wad
    

    Whether this is desirable or not may vary, but this is definitely something you need to be aware of. And not just you, but the players of your mod as well, since they’re the ones in control of the load order.

  2. This method only allows 1:1 replacements; if for some reason you want one class to replace several classes from the vanilla game, you’ll have to create copies of it just for the sake of replacement. This is undesirable, since this leads to needlessly messy code and potential issues.

A more robust way is to use an event handler. This method is described in the event handlers chapter; however, being able to use it requires knowing about virtual functions and event handlers, so I recommend that you continue reading the guide first.

What exactly does replacement do?

It’s important to be clear about one thing: “replacing” doesn’t mean actual replacement. The original class doesn’t stop existing. It’s still in the code.

What the replaces keyword (or the CheckReplacement() event handler function) does, is only one thing: it tells the engine which actor to spawn instead of which actor. This affects the actors pre-placed on a map, for example: they will be replaced with your actors if you use one of these methods. This also affects many dynamic spawning methods: e.g. it can replace projectiles (for example, if you create an actor that replaces Rocket, it’ll replace rockets fired by the Doom Rocket Launcher, as well as by Cyberdemon, since those are the same).

It’s not possible to completely delete classes from the game, because classes exist in a static context (which means they’re defined in the code, and they are compiled when GZDoom is started). Spawning, on the other hand, happens dynamically (by the engine, when it’s already running), so it can be overridden to spawn another actor instead.

There are also some special cases where spawning is forced, i.e. it disables replacement. One notable example of it is Doom 2’s BossBrain (Romero’s brain) actor, which, when killed, spawns a bunch of rocket explosions across the Icon of Sin’s face: those rocket explosions can’t be replaced even if you replace the Rocket actor, because they’re spawned by a method that disables replacement. This is, however, a special case, which you normally won’t run into when coding a mod.

Placing your classes in a custom map

If you’re working on a project that has its own maps, you don’t need to bother with replacements described in the previous subsection; you can simply place your actors in your map. To be able to do that, you need to do a few things.

Giving your actors editor numbers

To be placable on maps, your actors need DoomEdNums, also known as editor numbers. An editor number is a unique object identifier that actors need to have in order to be placeable on the map from a map editor. (Not to be confused with Spawn IDs or spawn numbers, they’re a completely different thing and are largely useless in modern GZDoom. You don’t need Spawn IDs to place your actors on the map.)

All existing GZDoom objects from all supported games have DoomEdNums: you can see them on the ZDoom wiki. If you give your own actors DoomEdNums, they have to be unique and different from the ones used by GZDoom.

DoomEdNums are given differently in DECORATE and ZScript. While you can attach DoomEdNums to actors directly in DECORATE, ZScript doesn’t support that. Instead they should be defined via MAPINFO.

The process is very simple. Let’s say you have a custom actor defined:

class BigZombieman : Zombieman
{
    Default
    {
        health 1000;
        scale 2;
        radius 40;
        height 40;
    }
}

Create a MAPINFO lump at the root of your project’s folder or archive and add:

DoomEdNums
{
    16000 = "BigZombieman"
}

Good! Move on to the next step.

Note, if an object is only meant to be manually spawned by another object (for example, it’s some kind of a special effects particle), they don’t need DoomEdNums. DoomEdNums are only necessary for objects that you should be able to place on your map from the Things menu, so that they’re there at map start.

Seeing your actors in a map editor

Now you will, naturally, need to get a map editor—the most current map editor for GZDoom is Ultimate Doom Builder. Note that only maps in ZDoom format support custom actors, so the recommended map format is GZDoom UDMF.

Do the following:

  1. Start UDB, choose File > New Map (or click Ctrl+N), or, if you already have a map, hit F2 to open the Map Options dialogue.
  2. Click Add Resource to open the Add Resource window.
  3. Choose the From PK3/PK7 tab and find and add gzdoom.pk3 as a resource.
  4. Before clicking OK, tick the Exclude this resource from testing parameters checkbox.
  5. Now you naturally need to add your classes as a resource. If your classes are defined in a PK3 or a structured folder, just add them as a resource using the suitable tab and hit OK.

Note: While it’s possible to store your classes in a WAD, it’s not recommended because WADs are very hard to organize. It’s preferable to keep your maps in a WAD (since they don’t support any other format), while keeping your code and assets in a PK3 or a folder.

Now place a thing on your map, right-click it, and if you scroll down the list of available thing categories, at the bottom you’ll see “User-defined”. There you will find your custom actors.

If you’re planning a big project with multiple classes and you want them to have custom categories, you can either create a custom UDB config, or you can use editor keys in the definitions of your classes: these are special comments that can be added to a class, so that UDB can parse them and apply the values. Editor keys must be placed in the Default block, and GZDoom itself ignores them, but the map editor is designed to read their values. Example:

class BigZombieman : Zombieman
{

    Default
    {
        //$Title "Big Zombieman"
        //$Angled
        //$Category "Monsters"
        health 1000;
        scale 1.5;
        radius 30;
        height 56;
        speed 6;
    }
}

This will make your custom monster appear in UDB in the “Monsters” category, with an arrow key showing its angle (so you can easily determine where it’ll be facing upon spawning) and it’ll also be named “Big Zombieman” rather than just use the class name.

Editor keys are covered in more detail on ZDoom Wiki.


🟢 «< BACK TO START

🔵 « Previous: Classes instead of actors 🔵 » Next: Anonymous functions