Modding:Tutorial - Snapjaw Mages

From Caves of Qud Wiki
Jump to navigation Jump to search

Introduction

Welcome! If you're looking to learn how to write mods for Caves of Qud, this tutorial is a good place to start.

In this tutorial, we're going to add a new creature to the game -- the great and mystical snapjaw mage. Along the way we're going to be helped by my friends Rulimispruce, legendary sprouting orb and Pyovya, legendary mopango.

Sprouting orb.png < Howdy!
Mopango pilgrim.png < I'm excited!

By the end of this tutorial, you'll have created a mod for Caves of Qud which

  • introduces two new creatures, the snapjaw fire mage and the snapjaw ice mage, and
  • introduces two new missile weapons, the tome of fire and the tome of ice.

Before you get started, you may also want to check out the Blue Ctesiphus modding tutorial on Steam. It's a little dated, but it also covers many of the concepts covered here.

This mod won't require any scripting, so you don't need to know anything about C#. However, I'd recommend that you play through a good chunk of Caves of Qud before you try your hand at modding. It's helpful to be able to refer to existing creatures and items when creating new ones.

Source code

You can find the source for the final version of the mod here: https://github.com/TrashMonks/Snapjaw-Mages

In particular (especially if you're familiar with version control), try stepping through the repository's commit history; it follows the steps involved in this tutorial, and you can use it to see how the mod changes after every step. Otherwise, if you find that for one reason or another your mod isn't working at some point in the tutorial, take a look at the files in the repository (which contain a complete, working implementation of the mod you'll be creating in this tutorial).

Getting started, and basic concepts

If this is your first time modding, make sure to follow the checklist in Modding:Overview. It's particularly important that you install a text editor like VS Code. I've prepared all of the tiles that you'll need to use for this tutorial, so you don't need to familiarize yourself with a pixel editor. However, if you plan on adding new tiles in the future you should be familiar with an editor like Piskel or Aseprite.

You should also enable the following options in your game:

  • Modding > Enable Mods
  • Debug > Show quickstart option during character generation.
    • This will cause a _Quickstart option to show up when you start a new game. This option makes it very easy to create a new character and immediately start testing out your mod.

Finally, you should take a look at the page of file locations and find the following directories on your computer:

  • the "offline mods" folder; and
  • the "game data files" folder.

XML

XML (eXtensible Markup Language) is a type of markup language, which (loosely speaking) is a text-based format that can be understood by a program. Caves of Qud makes extensive use of XML for things like defining creatures, quests, items, conversations, and more. We'll be working with XML a lot throughout this tutorial, so it's helpful to have a little familiarity with XML first.

You can check out the Wikipedia page on XML if you want to learn more. Here's some basic terminology you should be familiar with: in the XML snippet below,

<foo bar="baz" />

foo is an XML tag. Meanwhile, bar is an attribute, which (in this case) has been assigned the value baz. The /> at the end tells us that we should close the XML (i.e. pair it with a matching closing tag). The following two lines of XML are equivalent:

<foo bar="baz"/>
<foo bar="baz"></foo>

You must always close your XML tags! A common source of issues for beginning modders is that they don't close their tags, which causes their XML files to be interpreted incorrectly by the game.

You may also see comments here and there. A comment consists of one or more lines of text between <!-- and -->, such as the following:

<!-- This is a comment! -->

<!--
  ... and this is also a comment!
-->

Comments don't do anything by themselves; they're just helpful for documenting things that are going on in your XML.

Wishes

Wishes are commands that can be executed in-game to perform a variety of tasks. They're very useful when modding -- you can use a wish to spawn a creature of your choice, or create an item, and a variety of other tasks. You should follow the instructions on the Wishes page to bind the wish command to a key combination.

There are a lot of useful wishes. Here are a few that I use often:

  • idkfa: puts your character in "God Mode". This makes your character immortal, and causes all attacks that penetrate to instantly kill.
  • swap: allows you to swap your body with an adjacent creature.
  • In addition, you can wish for the ID of a creature or item to spawn that creature/item. For instance, wishing for Troll King 1 will spawn Jotun, Who Parts Limbs.

Navigating the game's data files

We won't delve too deeply into Caves of Qud's data files, but it's helpful to know how to navigate through them when creating a mod. Make sure that you've found where your computer keeps the game's data files by looking at the list of file locations before continuing; it should be a subfolder called Base of a folder called StreamingAssets somewhere on your machine.

Mopango pilgrim.png < Don't modify the game's files! If you make a mistake, you could end up having to reinstall the game.

Once you've found this folder, you should see a bunch of files with names ending in things like .bmp, .rpm, .xml, .json, and so on.

Snapjaw Mages -- Game files.webp

For this tutorial, the .xml files are the ones we're going to be the most interested in. There are a lot of these files (35 as of game version 2.0.205.63); here's a high-level overview of just a few of them:

  • Bodies.xml: in charge of defining various body parts and creature anatomies. This is useful when you want to define a new body type; for instance, the Blahajar mod defines a unique anatomy specific to sharks.
  • Conversations.xml: defines the conversational script that different creatures have.
  • Mutations.xml: data file containing all of the mutations that are available in-game.
  • ObjectBlueprints/Creatures.xml: definitions of all of the creatures that exist in Caves of Qud.
  • ObjectBlueprints/Items.xml: definitions of all of the items available in Caves of Qud.
  • PopulationTables.xml: contains population tables, which are used to figure out when and where creatures should spawn.
  • Skills.xml: contains all of the skill trees provided by Caves of Qud, and abilities within those trees.
  • Quests.xml: lays out the steps and rewards for non-procedurally generated quests.

When adding new creatures and items (as in this tutorial!), we're mostly going to be interested in just ObjectBlueprints/Creatures.xml and ObjectBlueprints/Items.xml. Starting in the next section, we'll discuss what all of the bits and pieces mean in these files. Make sure you know where to find these files before you continue!

Creating a mod

Mopango pilgrim.png < Let's get started!

Our first goal is to create a mod that doesn't actually do anything. Start by finding Caves of Qud's "offline mods" folder; check the file locations page to see where this folder is on your operating system. Inside this folder (it should be named Mods), create a subfolder called Snapjaw-Mages. The first file we're going to write for our mod is a manifest file; this contains metadata such as our mod's title, a description of the mod, and its version. Create a file called manifest.json inside the Snapjaw-Mages/ folder, and paste in the following contents:

{
    "id": "Pyovya_SnapjawMage",
    "title": "Snapjaw Mages!",
    "description": "Adds the new Snapjaw Mage creature to Caves of Qud.",
    "version": "0.1.0",
    "author": "Pyovya",
    "tags": "Creature",
    "PreviewImage": "preview.png"
}
Mopango pilgrim.png < Feel free to replace Pyovya with your own name!

We should also get a preview image for the mod. I've made one that you can use here:

https://github.com/TrashMonks/Snapjaw-Mages/blob/main/Snapjaw-Mages/preview.png

Download this image and place it in the SnapjawMage/ folder, so that now your folder looks like this:

Snapjaw-Mages
├── manifest.json
└── preview.png

Now start up Caves of Qud. In the splash screen, click on "Installed Mod Configuration". If everything went correctly, you should see a "Snapjaw Mages!" mod appear in your list of installed mods:

Snapjaw Heros -- Mods List.webp

You will be returning to this screen often!

Mopango pilgrim.png < Every time you make a change to your mod, you will need to go back to this screen and activate the "Save and Reload" option (which you can do by pressing r). You should see a bar saying "reloading mod configuration" appear at the bottom of your screen, like so:

Snapjaw Mages -- reloading mod config.webp

Your first creature

Adding a new creature to Creatures.xml

In your mod directory, create a folder called ObjectBlueprints/, and within that folder open up a file in your text editor called Creatures.xml. Let's start by adding the following to Creatures.xml:

<?xml version="1.0" encoding="utf-8" ?>
<objects>
</objects>

Save this file! Before continuing, make sure your mod directory looks something like this:

Snapjaw-Mages
├── manifest.json
├── ObjectBlueprints
│   └── Creatures.xml
└── preview.png

With that out of the way, let's start adding our snapjaw mage to Creatures.xml:

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object Name="Pyovya_SnapjawMage_Snapjaw Mage" Inherits="Snapjaw">
  </object>
</objects>

Let's break this down:

  • The object tag tells Qud that we want to add a new creature.
  • The Name attribute is a unique identifier for our new creature (it's not the name of the creature as it appears in-game -- we'll get to that shortly). You'll use this identifier whenever you want to wish for a snapjaw mage.
  • The Inherits attribute says that our new creature should "inherit" all of the properties of the Snapjaw creature.
Sprouting orb.png < Hey, why'd you add the Pyovya_SnapjawMage bit to the front of the name of the creature? Why not just call it a Snapjaw Mage?
Mopango pilgrim.png < When writing a mod, it's common courtesy to prefix unique identifiers with your name, an underscore _, followed by the mod's name. This ensures that if somebody else writes their own Snapjaw Mage mod, your mod won't conflict with theirs.

So far we haven't actually added anything to our creature; for all intents and purposes it's a plain ol' snapjaw. Let's change that by adding some parts.

Sprouting orb.png < What's a part?
Mopango pilgrim.png < A part is a special piece of code that can be added to a creature to modify its appearance, behavior, stats, and any number of other things. There are a lot of parts -- over a thousand! You won't need to worry about most of them; many of them are highly-customized bits of code written for just one or two creatures. For instance, there's a CryptSitterBehavior part that is used exclusively by crypt sitters.

We're going to add two parts to our creature: Render and Description.

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object Name="Pyovya_SnapjawMage_Snapjaw Mage" Inherits="Snapjaw">
    <part Name="Description" Short="A tattered robe and decaying hat are all that protect =pronouns.possessive= thin layer of grizzled fur from the forces of nature. But behind that furtive glance, =pronouns.subjective= =verb:prepare:afterpronoun= an elemental spell, deployed at a moment's notice against threats to =pronouns.objective= and =pronouns.possessive= kin. =pronouns.Subjective= =verb:understand:afterpronoun= not the power that =pronouns.subjective= =verb:wield:afterpronoun=; and that only makes =pronouns.objective= all the more dangerous." />
    <part Name="Render" DisplayName="snapjaw mage" Tile="Assets_Content_Textures_Creatures_sw_snapjaw_warrior.bmp" ColorString="&amp;O" DetailColor="Y" />
  </object>
</objects>

Before I dig into the XML we've just added, save Creatures.xml. Reload your mods, and then wish for a Pyovya_SnapjawMage_Snapjaw Mage.

Mopango pilgrim.png < To reload your mods, return to the start screen for Caves of Qud, click on "installed mod configuration", and then press r. Then restart the game, either by continuing an old save or starting a new save.

You should see a snapjaw like the one below show up:

Snapjaw Mages -- Initial mage.webp

Congrats! You've successfully added a snapjaw mage to your game.

Sprouting orb.png < I did?

Well, it's still a little bare-bones, but you have indeed created a new creature called a snapjaw mage and spawned it in your game! Give yourself a pat on the back, Rulimispruce.

Sprouting orb.png < *rustling noises*

Now, back to the parts that you just added:

  • The Description part changes (as you might guess) the description of the creature when you look at it.
    • You'll notice that the description includes a lot of things like =pronouns.subjective= and =verb:understand:afterpronoun=. This ensures that the game uses the correct pronouns while generating the creature's description and conjugates verbs correctly. See Modding:Grammar for more details.
  • The Render attribute changes the creature's appearance, as well as its name (as it appears in-game). This tag has the following attributes:
    • DisplayName: how the creature's name is displayed in-game.
    • Tile: a .png or .bmp ("bitmap") image that's used to display the creature. Check out Modding:Tiles for more information. In the snippet above, I just used the tile for snapjaw warriors to get us started.
    • ColorString and DetailColor: these are the primary and secondary colors used to color in the tile that you supply. You can look at Modding:Colors & Object Rendering for a full list of all of the available colors; in this case, we used O (orange) as the primary color and Y (white) as the detail color.

Using custom tiles

Instead of having our snapjaw mages look like off-color snapjaw warriors, let's add a snazzy new tile to the game to use for our mages! Just pop open a pixel editor, create a 16px x 24px black-and-white tile, save it as a --

Sprouting orb.png < I'm just a lil old sprouting orb, I don't know how to do any of those things.

Ah, alright, fair enough. Well in that case, feel free to download the tile that I've already made for you here:

https://github.com/TrashMonks/Snapjaw-Mages/blob/main/Snapjaw-Mages/Textures/Pyovya_SnapjawMage/snapjaw_mage.png

In your mod directory, create a folder called Textures/; in that folder, create a subfolder called Pyovya_SnapjawMage/. Save snapjaw_mage.png there, so that your mod folder now looks like this:

Snapjaw-Mage
├── manifest.json
├── ObjectBlueprints
│   └── Creatures.xml
├── preview.png
└── Textures
    └── Pyovya_SnapjawMage
        └── snapjaw_mage.png

Let's change the Render part for our mages to use this tile:

<part Name="Render" DisplayName="snapjaw mage" Tile="Pyovya_Snapjaw/snapjaw_mage.png" ColorString="&amp;O" DetailColor="Y" />

Now let's fill out some more details about our furry friend!

Adding inventory

First of all, like any good mage, they need a walking stick and some books. Let's also give them some clothes to equip while we're at it. Add the following XML to your creature:

<inventoryobject Blueprint="Walking Stick" Number="1" />
<inventoryobject Blueprint="Cloth Robe" Number="1" />
<inventoryobject Blueprint="Sandals" Number="1" />
<inventoryobject Blueprint="StandaloneMarkovBook" Number="1-2" Chance="10" />

By setting Number="1-2" and Chance="10" in that last line, we've given our mages a 10% chance of holding 1-2 books when they spawn.

Adding skills

Let's give our mage the Cudgel skill as well, so that they're a little bit better at fighting with that walking stick:

<skill Name="Cudgel" />

Changing stats

The base Snapjaw creature that we're inheriting from is pretty weak, so we're going to bump up our mage's stats a little.

<stat Name="Hitpoints" Value="15" />
<stat Name="DV" Value="4" />

<!-- Raise ego to make the mage's mental mutations a little more powerful -->
<stat Name="Ego" sValue="19" />

<!-- Raise willpower to reduce the cooldowns of abilities -->
<stat Name="Willpower" sValue="19" />

If you've followed along up to this point, you should be ending this section with a snapjaw mage that looks like this:

Snapjaw Mages -- Mage new tile.webp

Your Creatures.xml at this point should look something like the following. Make sure that everything checks out before proceeding to the next section!

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object Name="Pyovya_SnapjawMage_Snapjaw Mage" Inherits="Snapjaw">
    <part Name="Description" Short="A tattered robe and decaying hat are all that protect =pronouns.possessive= thin layer of grizzled fur from the forces of nature. But behind that furtive glance, =pronouns.subjective= =verb:prepare:afterpronoun= an elemental spell, deployed at a moment's notice against threats to =pronouns.objective= and =pronouns.possessive= kin. =pronouns.Subjective= =verb:understand:afterpronoun= not the power that =pronouns.subjective= =verb:wield:afterpronoun=; and that only makes =pronouns.objective= all the more dangerous." />
    <part Name="Render" DisplayName="snapjaw mage" Tile="Pyovya_SnapjawMage/snapjaw_mage.png" ColorString="&amp;O" DetailColor="Y" />

    <inventoryobject Blueprint="Walking Stick" Number="1" />
    <inventoryobject Blueprint="Cloth Robe" Number="1" />
    <inventoryobject Blueprint="Sandals" Number="1" />
    <inventoryobject Blueprint="StandaloneMarkovBook" Number="1-2" Chance="10" />

    <skill Name="Cudgel" />

    <stat Name="Hitpoints" Value="15" />
    <stat Name="DV" Value="4" />
    <stat Name="Ego" sValue="19" />
    <stat Name="Willpower" sValue="19" />
  </object>
</objects>

Using your creature as a base: Elemental Mages

Sprouting orb.png < Hold up a second. How is this creature a mage? It doesn't have any kind of magic spells!

I was just getting to that!

So far, we've sketched out most of the properties, skills, and stats that a snapjaw mage should have. Now we're going to create two types of mages: fire mages and ice mages.

First, we need to mark the snapjaw mage we've created as a "base object", i.e. an object that doesn't appear in-game but which instead serves as the basis for other objects. Add the following tag to the Pyovya_SnapjawMage_Snapjaw Mage creature:

<tag Name="BaseObject" Value="*noinherit" />

Now, below your definition of the snapjaw mage in Creatures.xml, add two new creatures:

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <!--
  Skipped: the XML that defines the "Pyovya_SnapjawMage_Snapjaw Mage" creature
  -->

  <object Name="Pyovya_SnapjawMage_Fire Mage" Inherits="Pyovya_SnapjawMage_Snapjaw Mage">
  </object>

  <object Name="Pyovya_SnapjawMage_Ice Mage" Inherits="Pyovya_SnapjawMage_Snapjaw Mage">
  </object>
</objects>
Mopango pilgrim.png < Notice how we've used the Inherits attribute here. We're telling the game that our original snapjaw mage creature, Pyovya_SnapjawMage_Snapjaw Mage, should serve as a base for our fire and ice mages. As a result, our creatures will spawn with walking sticks, have the Cudgel skill, and so on.

Referencing the Modding:Colors & Object Rendering page, we're going to change our fire mages' detail color to red. We're also going to give them the Pyrokinesis mutation:

<object Name="Pyovya_SnapjawMage_Fire Mage" Inherits="Pyovya_SnapjawMage_Snapjaw Mage">
  <part Name="Render" DisplayName="snapjaw {{R|fire}} mage" Tile="Pyovya_SnapjawMage/snapjaw_mage.png" ColorString="&amp;O" DetailColor="R" />
  <mutation Name="Pyrokinesis" Level="1" />
</object>

The {{R|fire}} bit there says that the word "fire" should be colored red. Now we'll make some similar changes to ice mages (changing their color to cyan), and give them the Cryokinesis mutation.

<object Name="Pyovya_SnapjawMage_Ice Mage" Inherits="Pyovya_SnapjawMage_Snapjaw Mage">
  <part Name="Render" DisplayName="snapjaw {{C|ice}} mage" Tile="Pyovya_SnapjawMage/snapjaw_mage.png" ColorString="&amp;O" DetailColor="C" />
  <mutation Name="Cryokinesis" Level="1" />
</object>

Now try wishing for a Pyovya_SnapjawMage_Fire Mage and a Pyovya_SnapjawMage_Ice Mage. You should get two creatures that appear like the ones below (and they should immediately start attacking you with Pyrokinesis and Cryokinesis!):

Snapjaw Mages -- Fire and Ice.webp

Creating new items

We've now successfully added a snapjaw fire mage and a snapjaw ice mage. We're also going to give them two new missile weapons: a tome of fire and a tome of ice. These weapons will be magical spell books that function as less powerful versions of the flamethrower and freeze ray, respectively.

Start by creating a file Items.xml in the ObjectBlueprints/ folder:

Snapjaw-Mages
├── manifest.json
├── ObjectBlueprints
│   ├── Creatures.xml
│   └── Items.xml
├── preview.png
└── Textures
    └── Pyovya_SnapjawMage
        └── snapjaw_mage.png
Sprouting orb.png < Does it matter whether I name my file Items.xml? And do I have to place it in the ObjectBlueprints/ directory?
Mopango pilgrim.png < Nope and nope! In fact, you can name all of your XML files whatever you want (as long as they end in .xml) and place them wherever you feel like (as long as they're in the SnapjawMage/ mod directory). The scheme that we're using here to organize our XML files is the same scheme that the game uses for its own files (the ones that you can find in the "game data files" folder), just to make the relationship between what we're writing and Caves of Qud's code a little bit clearer.

We want to create two new items -- a fire tome and and ice tome -- and we can probably guess in advance that these two items are going to have a lot of the same properties. Therefore, we're going to start the same way that we did when we were creating our mages: we'll create a base "magic tome" object, which we will later inherit from.

Add the following to Items.xml:

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object Name="Pyovya_SnapjawMage_Magic Tome" Inherits="BaseMissileWeapon">
    <tag Name="BaseObject" Value="*noinherit" />
  </object>
</objects>

Now we need to add some parts to our object:

<!-- We borrow the same description that StandaloneMarkovBook uses -->
<part Name="Description" Short="Crisp pages of goatskin vellum are bound into a codex." />
<part Name="Physics" Weight="1" UsesTwoSlots="true" />
<part Name="Commerce" Value="80" />

We've already seen the Description part before. The Physics part is a common part used by items to determine how much they weigh, whether they are equipped in one or two slots, and what category they appear under in the inventory screen. Finally, the Commerce part defines how valuable the item is.

Let's also add a tag to the item to indicate its tier, as well as what mods it can accept (in our case, we don't want this item to be moddable):

<tag Name="Tier" Value="2" />
<tag Name="Mods" Value="" />

That should be enough for our base object. Now, let's create a fire tome and an ice tome by inherting from Pvovya_SnapjawMage_Magic Tome. While we're at it, let's add some tiles to render each of them; we'll use the built-in Items/sw_book_1.bmp tile (which looks like a regular book).

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <!-- Skipped: definition of "Pyovya_SnapjawMage_Magic Tome" -->

  <object Name="Pyovya_SnapjawMage_Fire Tome" Inherits="Pyovya_SnapjawMage_Magic Tome">
    <part Name="Render" DisplayName="tome of {{R|fire}}" Tile="Items/sw_book_1.bmp" ColorString="&amp;R" DetailColor="Y" />
  </object>

  <object Name="Pyovya_SnapjawMage_Ice Tome" Inherits="Pyovya_SnapjawMage_Magic Tome">
    <part Name="Render" DisplayName="tome of {{C|ice}}" Tile="Items/sw_book_1.bmp" ColorString="&amp;C" DetailColor="Y" />
  </object>
</objects>

Using existing items as a template

Now, we know that we want these items to function like a flamethrower and freeze ray, but we probably need to iron out some more details first. Such as:

Sprouting orb.png < Do magic books use ammo?

That's a good question. If we wanted, we could set up our new missile weapons to use some kind of magical ammo, or pull from an energy cell. I'm actually going to go with a third option -- let's set up our tomes of fire and ice so that they don't use any kind of ammo or energy, but instead just have a plain 10-turn cooldown.

We must also figure out what kind of projectile each of these items shoots. Let's take a look at how the game defines the flamethrower and freeze ray objects. Looking at the file locations, navigate in your file browser to where the game's data files are stored. Once you're in that folder, go to ObjectBlueprints/ and open up the Items.xml file. Then search for Flamethrower and Freeze Ray; you should find the following XML for each of them.

<object Name="Freeze Ray" Inherits="BaseRifle">
  <part Name="Render" DisplayName="{{freezing|freeze}} ray" Tile="items/sw_raygun.bmp" ColorString="&amp;C" DetailColor="y" />
  <part Name="Physics" UsesTwoSlots="true" Weight="28" />
  <part Name="MissileWeapon" Skill="Rifle" AmmoChar="FR" ShotsPerAction="1" AmmoPerAction="1" ShotsPerAnimation="1" WeaponAccuracy="0" />
  <part Name="Commerce" Value="750" />
  <part Name="EnergyAmmoLoader" ChargeUse="500" ProjectileObject="ProjectileFreezeRay" />
  <part Name="EnergyCellSocket" SlotType="EnergyCell" />
  <part Name="Description" Short="Gaseous coolant billows through a chiffon tube, putting dew on the wide and planar barrel chrome. It spills out onto the air from a ice-chipped muzzle." />
  <part Name="Examiner" Complexity="5" />
  <part Name="TinkerItem" Bits="12345" CanDisassemble="true" CanBuild="true" />
  <part Name="Metal" />
  <part Name="ItemElements" Elements="ice:10" />
  <tag Name="MissileFireSound" Value="lazerMedium4" />
  <tag Name="Mods" Value="MissileWeaponMods,FirearmMods,CommonMods,RifleMods,ElectronicsMods,BeamWeaponMods" />
  <tag Name="Tier" Value="5" />
  <tag Name="DynamicObjectsTable:Guns" />
  <stag Name="Cold" />
</object>

<!-- some more item definitions later... -->

<object Name="Flamethrower" Inherits="BaseHeavyWeapon">
  <part Name="Render" Tile="items/sw_flamethrower.bmp" DisplayName="flamethrower" ColorString="&amp;R" TileColor="&amp;c" DetailColor="R"/>
  <part Name="Physics" UsesTwoSlots="true" />
  <part Name="MissileWeapon" Skill="HeavyWeapons" bShowShotsPerAction="false" NoWildfire="true" MaxRange="12" AnimationDelay="20" AmmoChar="f" ShotsPerAction="9" AmmoPerAction="1" ShotsPerAnimation="9" WeaponAccuracy="10" />
  <part Name="Commerce" Value="100" />
  <part Name="LiquidAmmoLoader" Liquid="oil" ProjectileObject="ProjectileFlamethrower" />
  <part Name="LiquidVolume" MaxVolume="32" Volume="32" StartVolume="1d32" InitialLiquid="oil-1000" />
  <part Name="LeakWhenBroken" />
  <part Name="Description" Short="A limpid fuel pump exposes ribbons of oil slick, and behind it a voltage gauge ticks. The slanted heat shield is scorched black." />
  <part Name="Examiner" Alternate="UnknownBackpack" Complexity="4" />
  <part Name="TinkerItem" Bits="0004" CanDisassemble="true" CanBuild="true" />
  <part Name="Metal" />
  <tag Name="TurretName" Value="flamethrower turret" />
  <tag Name="Tier" Value="4" />
  <tag Name="UsesSlots" Value="Back,Missile Weapon,Missile Weapon" />
  <tag Name="DynamicObjectsTable:Guns" />
  <tag Name="ReloadSound" Value="SplashStep1" />
  <tag Name="MissileFireSound" Value="flamethrower" />
  <stag Name="Heat" />
</object>

If you know a bit about each of these items, you can intuit what some of these parts do. For instance, EnergyCellSocket is clearly related to the ability to insert and use energy cells in freeze rays; LiquidAmmoLoader is somehow connected to the fact that flamethrowers run on oil.

In any case, the most important bits right now is this line from the definition of Freeze Ray:

<part Name="EnergyAmmoLoader" ChargeUse="500" ProjectileObject="ProjectileFreezeRay" />

... and this line from the definition of Flamethrower:

<part Name="LiquidAmmoLoader" Liquid="oil" ProjectileObject="ProjectileFlamethrower" />

From this, we can see that freeze rays shoot a ProjectileFreezeRay object, and flamethrowers shoot a ProjectileFlamethrower object.

To have our new items shoot these projectiles on a cooldown, we're going to add the CooldownAmmoLoader part. Here's how we'll add it to our tome of fire:

<object Name="Pyovya_SnapjawMage_Fire Tome" Inherits="Pyovya_SnapjawMage_Magic Tome">
  <part Name="Render" DisplayName="tome of {{R|fire}}" Tile="Items/sw_book_1.bmp" ColorString="&amp;R" DetailColor="Y" />
  <part Name="CooldownAmmoLoader" Cooldown="10" Readout="true" ProjectileObject="ProjectileFlamethrower" />
</object>

... and here's how we'll add it to our tome of ice:

<object Name="Pyovya_SnapjawMage_Ice Tome" Inherits="Pyovya_SnapjawMage_Magic Tome">
  <part Name="Render" DisplayName="tome of {{C|ice}}" Tile="Items/sw_book_1.bmp" ColorString="&amp;C" DetailColor="Y" />
  <part Name="CooldownAmmoLoader" Cooldown="10" Readout="true" ProjectileObject="ProjectileFreezeRay" />
</object>

We also need to add a MissileWeapon part to each of our items, and specify the sound that the item makes when it's fired. Go ahead and update Items.xml until you get the following:

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <!-- Skipped: definition of "Pyovya_SnapjawMage_Magic Tome" -->

  <object Name="Pyovya_SnapjawMage_Fire Tome" Inherits="Pyovya_SnapjawMage_Magic Tome">
    <part Name="Render" DisplayName="tome of {{R|fire}}" Tile="Items/sw_book_1.bmp" ColorString="&amp;R" DetailColor="Y" />
    <part Name="MissileWeapon" AmmoChar="f" ShotsPerAction="1" AmmoPerAction="1" ShotsPerAnimation="1" WeaponAccuracy="0" />
    <part Name="CooldownAmmoLoader" Cooldown="10" Readout="true" ProjectileObject="ProjectileFlamethrower" />

    <tag Name="MissileFireSound" Value="flamethrower" />
  </object>

  <object Name="Pyovya_SnapjawMage_Ice Tome" Inherits="Pyovya_SnapjawMage_Magic Tome">
    <part Name="Render" DisplayName="tome of {{C|ice}}" Tile="Items/sw_book_1.bmp" ColorString="&amp;C" DetailColor="Y" />
    <part Name="MissileWeapon" AmmoChar="FR" ShotsPerAction="1" AmmoPerAction="1" ShotsPerAnimation="1" WeaponAccuracy="0" />
    <part Name="CooldownAmmoLoader" Cooldown="10" Readout="true" ProjectileObject="ProjectileFreezeRay" />

    <tag Name="MissileFireSound" Value="hiss_low" />
  </object>
</objects>

You've successfully added both items to your game! You can now wish for a Pyovya_SnapjawMage_Fire Tome or a Pyovya_SnapjawMage_Ice Tome.

Snapjaw Mages -- fire ice tomes.webp

Try equipping the item and firing it as a missile weapon. You should see a streak of fire appear when you shoot the tome of fire, and a streak of ice when you shoot the tome of ice.

Snapjaw Mages -- fire tome activated.webp

Snapjaw Mages -- ice tome activated.webp

Adding custom projectiles

Sprouting orb.png < I burnt down all of Joppa with my tome of fire! Take that, Yrame.

Ah, hm, yeah. The projectile that a missile weapon shoots defines the damage and effects of that weapon. We just decided to use the same projectile that flamethrower and freeze ray use, which in retrospect wasn't the greatest idea. Those are both mid- to late-game items, and we want our new snapjaws to be early-game enemies.

Let's fix that by creating some new projectile types that operate in the same way as ProjectileFlamethrower and FreezeRayProjectile, but are a little bit weaker. First, let's look up how these projectiles are defined in the game's data files; you can find them in (the game's version of) ObjectBlueprints/Items.xml.

We'll copy those definitions over to our own Items.xml in the folder for our mod. We'll rename these projectiles Pyovya_SnapjawMage_FireTomeProjectile and Pyovya_SnapjawMage_IceTomeProjectile:

<!-- Copied from ProjectileFlamethrower -->
<object Name="Pyovya_SnapjawMage_FireTomeProjectile" Inherits="TemporaryProjectile">
  <part Name="Render" DisplayName="{{R|stream of flame}}" ColorString="&amp;R" />
  <part Name="Projectile" BasePenetration="15" BaseDamage="1d2" Attributes="Heat Fire" ColorString="&amp;R" PassByVerb="whoosh" />
  <part Name="TemperatureOnHit" Amount="4d20" Max="false" OnWielderHit="true" />
  <part Name="TemperatureOnEntering" Amount="8d20" Max="false" OnWielderHit="true" />
</object>

<!-- Copied from ProjectileFreezeRay -->
<object Name="Pyovya_SnapjawMage_IceTomeProjectile" Inherits="TemporaryProjectile">
  <part Name="Render" DisplayName="{{C|energy beam}}" ColorString="&amp;B" />
  <part Name="Projectile" BaseDamage="1d4" Attributes="Cold NonPenetrating" ColorString="&amp;B" PassByVerb="crackle" />
  <part Name="TemperatureOnHit" Amount="-190" Max="false" OnWielderHit="true" />
  <part Name="TemperatureOnEntering" Amount="-190" Max="false" OnWielderHit="true" />
</object>

Let's make some changes to the penetration, base damage, and temperature changes of each of these items. Here's what I set mine to, but go ahead and try tweaking these numbers to different values!

<object Name="Pyovya_SnapjawMage_ProjectileFireTome" Inherits="TemporaryProjectile">
  <part Name="Render" DisplayName="{{R|stream of flame}}" ColorString="&amp;R" />
  <part Name="Projectile" BasePenetration="4" BaseDamage="1d4" Attributes="Heat Fire" ColorString="&amp;R" PassByVerb="whoosh" />
  <part Name="TemperatureOnHit" Amount="4d20" Max="false" OnWielderHit="true" />
  <part Name="TemperatureOnEntering" Amount="8d20" Max="false" OnWielderHit="true" />
</object>

<object Name="Pyovya_SnapjawMage_ProjectileIceTome" Inherits="TemporaryProjectile">
  <part Name="Render" DisplayName="{{C|streak of ice}}" ColorString="&amp;B" />
  <part Name="Projectile" BasePenetration="4" BaseDamage="1d4" Attributes="Cold" ColorString="&amp;B" PassByVerb="crackle" />
  <part Name="TemperatureOnHit" Amount="-4d20" Max="false" OnWielderHit="true" />
  <part Name="TemperatureOnEntering" Amount="-8d20" Max="false" OnWielderHit="true" />
</object>

Now you should change the CooldownAmmoLoader part of your tome of fire and tome of ice objects to use this new projectile.

Wrapping up

We can't forget to give our mages their new weapons! Go back to Creatures.xml, and add the following XML tag to Pyovya_SnapjawMage_Fire Mage:

<inventoryobject Blueprint="Pyovya_SnapjawMage_Fire Tome" Number="1" />

You should also add the tome of ice to Pyovya_SnapjawMage_Fire Mage's inventory:

<inventoryobject Blueprint="Pyovya_SnapjawMage_Ice Tome" Number="1" />

Now when you spawn one of your mages, you should find that they already have their magical tomes equipped.

Snapjaw Mages -- mage tome equipped.webp

Controlling spawning: population tables

Sprouting orb.png < Well, it looks like I can wish for a Pyovya_SnapjawMage_Fire Tome or a Pyovya_SnapjawMage_Ice Mage. But I can't find them anywhere in the game!

That's because there's one thing we're still missing: we haven't told Caves of Qud when and where our new mages should spawn!

Mopango pilgrim.png < In fact, it is already possible for our snapjaw mages to appear in game; it's just that the probability of this happening is very low at the moment. We'll talk about this more when we discuss dynamic tables.

Caves of Qud uses population tables to decide when to spawn different items and creatures. Here's an example of one such population table (which you can find in the game data in PopulationTables.xml):

<population Name="SaltMarshZoneGlobals">
  <table Name="StandardSurfaceGlobals" />
  <object Chance="90" Number="160-200" Blueprint="Watervine" />
  <object Chance="90" Number="90-130" Blueprint="Brinestalk" />
  <object Chance="50" Number="6-16" Blueprint="Dogthorn Tree" />
  <object Chance="25" Number="1-4" Blueprint="Witchwood Tree" />
  <object Chance="5" Number="1-4" Blueprint="Starapple Tree" />

  <object Chance="50" Blueprint="Croc" />
  <object Chance="75" Number="2d6" Blueprint ="Glowpad" Hint="Aquatic" />
  <object Chance="75" Number="2d4" Blueprint ="Glowfish" Hint="Aquatic" />
  <object Chance="50" Number="2d6" Blueprint ="GiantDragonfly" />
  <group Chance="4" Name="ManySnapjaws" Style="pickeach">
    <table Name="SnapjawParty1" Number="4" />
  </group>
</population>

This table is used in the salt marsh to help decide what animals and plants should be spawned. Let's take a look at a few of these lines:

  • <table Name="StandardSurfaceGlobals" />: this says that the SaltMarshZoneGlobals table should include all of the contents of another table called StandardSurfaceGlobals. You can find the definition of that table in PopulationTables.xml; it does things like add a chance of a Mechanimist pilgrim spawning in each zone.
  • <object Chance="50" Blueprint="Croc" />: this adds a 50% chance of a croc spawning in a given zone.
  • <group Chance="4" Name="ManySnapjaws" Style="pickeach">...</group>: this defines a "group" in the table. The usage here says (roughly speaking) that there's a 4% chance that we sample some creatures from the SnapjawParty1 table.

Adding snapjaw mages to population tables

There are a few different snapjaw-related population tables in the data files -- SnapjawParty0, SnapjawParty1, SnapjawParty1-with-Warlord, and SnapjawParty2. Each of these tables is used for different encounters with snapjaws.

In your Snapjaw-Mages mod folder, create a new file called PopulationTables.xml:

Snapjaw-Mages
├── manifest.json
├── ObjectBlueprints
│   ├── Creatures.xml
│   └── Items.xml
├── PopulationTables.xml
├── preview.png
└── Textures
    └── Pyovya_SnapjawMage
        └── snapjaw_mage.png

Within this file, start by adding the following:

<?xml version="1.0" encoding="utf-8" ?>
<populations>
</populations>

Next, we need to modify the contents of the four tables that we listed previously:

<?xml version="1.0" encoding="utf-8" ?>
<populations>
  <population Name="SnapjawParty0" Load="Merge">
  </population>
  <population Name="SnapjawParty1" Load="Merge">
  </population>
  <population Name="SnapjawParty1-with-Warlord" Load="Merge">
  </population>
  <population Name="SnapjawParty0" Load="Merge">
  </population>
</populations>
Sprouting orb.png < What does Load="Merge" do?
Mopango pilgrim.png < We need to specify Load="Merge" to tell Caves of Qud that we want to merge new XML into an existing table. This prevents us from creating a new population table like (for example) SnapjawParty0 that overwrites the old one.

It's possible to use Load="Merge" to modify the contents of most of the game's XML files.

Each of these tables has a group named Creatures, for instance:

<population Name="SnapjawParty0">
  <group Name="Creatures" Style="pickeach">
    <object Chance="100" Number="1-3" Blueprint="Snapjaw Scavenger 0" />
    <object Chance="75" Number="1-3" Blueprint="Snapjaw Scavenger 0" />
    <object Chance="25" Number="1-3" Blueprint="Snapjaw Hunter 0" />
    <object Chance="2" Number="1" Blueprint="Snapjaw Shotgunner 0" />
    <object Chance="15" Number="1" Blueprint="Snapjaw Brute 0" />
    <object Chance="10,3" Number="1" Blueprint="Snapjaw Warrior 0" />
    <object Chance="10" Number="1" Blueprint="Snapjaw Warlord 0" />
    <object Chance="3" Number="1" Blueprint="Snapjaw Hero 0" />
    <table Name="HumanoidEnvironmentFeatures" Chance="25" />
    <table Name="SnapjawParty0" Chance="10" />
  </group>
</population>

Because the group has Style="pickeach", the population table will go over each item (each XML tag) in the group and sample creatures based on the rules defined by that item:

  • Based on the first line (<object Chance="100" Number="1-3" Blueprint="Snapjaw Scavenger 0" />), there's a 100% chance that 1-3 snapjaw scavengers spawn).
  • The second line (<object Chance="75" Number="1-3" Blueprint="Snapjaw Scavenger 0" />) adds a 75% chance for an additional 1-3 snapjaw scavengers to spawn.
  • The fifth line (<object Chance="15" Number="1" Blueprint="Snapjaw Brute 0" />) adds a 15% chance for a snapjaw brute to spawn.
  • The last line (<table Name="SnapjawParty0" Chance="10" />) adds a 10% chance for another snapjaw party (also sampled from the SnapjawParty0 table) to spawn alongside this party.

To guarantee that each snapjaw party spawns with a fire mage and an ice mage, we'll merge some new XML into each of these groups:

<?xml version="1.0" encoding="utf-8" ?>
<populations>
  <population Name="SnapjawParty0" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Ice Mage" />
    </group>
  </population>

  <population Name="SnapjawParty1" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Ice Mage" />
    </group>
  </population>

  <population Name="SnapjawParty1-with-Warlord" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Ice Mage" />
    </group>
  </population>

  <population Name="SnapjawParty0" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Ice Mage" />
    </group>
  </population>
</populations>

Now when you encounter a band of roving snapjaws, they should include one fire mage and one ice mage:

Snapjaw Mages -- snapjaw party.webp

You can use the population:findblueprint wish to check the likelihood that a snapjaw mage will spawn in each of the population tables. For instance, here's a snippet from the popup that appears when you wish for population:findblueprint:Pyovya_SnapjawMage_Fire Mage:

Snapjaw Mages -- poptable probs.webp

As a result of the XML we merged into PopulationTables.xml, our mage has a 100% chance of appearing in each of the SnapjawMage tables. But we can also see that it has a nonzero chance of appearing in many other population tables; for instance, it has a 97% chance generating inside the waterlogged tunnel. That's because the SnapjawParty tables are used in turn by many other population tables for areas where snapjaws may be generated.

Creating new tables

Having two mages appear alongside every party of snapjaws feels like a lot -- ideally, we'd have either one fire mage or one ice mage show up. We could try using Chance to reduce the probability of each type of mage showing up, for example:

<object Number="1" Chance="50" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
<object Number="1" Chance="50" Blueprint="Pyovya_SnapjawMage_Ice Mage" />

But all that does is give us a 50% chance of spawning a fire mage, and a 50% chance of spawning an ice mage. That means that there's a 25% chance that both mages show up, and a 25% chance that neither mage shows up.

Instead, we'll define a new population table. This table will be set up so that whenever we sample from it, we either pick a fire mage or an ice mage:

<population Name="Pyovya_SnapjawMage_Mages">
  <group Name="Mages" Style="pickone">
    <object Number="1" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
    <object Number="1" Blueprint="Pyovya_SnapjawMage_Ice Mage" />
  </group>
</population>

By giving the Mages group Style="pickone", we're telling the game "whenever we pick an item from the Pyovya_SnapjawMage_Mages table, we want to pick just one of the items listed in our Mages group". Now we can change the XML that we merged into the SnapjawParty tables to sample from our new table instead. Our final Populations.xml table will look like this:

<?xml version="1.0" encoding="utf-8" ?>
<populations>
  <population Name="Pyovya_SnapjawMage_Mages">
    <group Name="Mages" Style="pickone">
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Fire Mage" />
      <object Number="1" Blueprint="Pyovya_SnapjawMage_Ice Mage" />
    </group>
  </population>

  <population Name="SnapjawParty0" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <table Name="Pyovya_SnapjawMage_Mages" />
    </group>
  </population>

  <population Name="SnapjawParty1" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <table Name="Pyovya_SnapjawMage_Mages" />
    </group>
  </population>

  <population Name="SnapjawParty1-with-Warlord" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <table Name="Pyovya_SnapjawMage_Mages" />
    </group>
  </population>

  <population Name="SnapjawParty0" Load="Merge">
    <group Name="Creatures" Load="Merge">
      <table Name="Pyovya_SnapjawMage_Mages" />
    </group>
  </population>
</populations>

After this change, snapjaw parties that we run across will only have one mage, rather than two.

Dynamic population tables

So far, the tables we've been working with have been static -- they're pre-defined based on the contents of PopulationTables.xml. It's also possible to generate a dynamic population table, which is generated based on the tags that creatures have as well as their relationship to one another. For instance, let's take a look at the definition of shrewd baboon in the game's ObjectBlueprints/Creatures.xml:

<object Name="Shrewd Baboon" Inherits="Baboon">
  <part Name="Render" DisplayName="shrewd baboon" ColorString="&amp;B" />
  <stat Name="AV" Value="3" />
  <stat Name="Intelligence" Boost="1" />
  <stat Name="Hitpoints" Value="20" />
  <property Name="Role" Value="Leader" />
  <tag Name="DynamicObjectsTable:Baboons" />
</object>

The <tag Name="DynamicObjectsTable:Baboons" /> XML at the end adds the shrewd baboon to the "Baboons" dynamic table.

Caves of Qud primarily uses dynamic tables when it needs to be be able to get one of a particular type of item or creature -- for instance, a random type of energy cell or a random type of goatfolk. This is useful, for example, for identifying what creatures are able to own a lair.

There are several ways to create a dynamic table:

  • Through DynamicObjectsTable, such as the DynamicObjectsTable:Baboons table in the previous example. These tables contain all objects explicitly tagged as belonging to that table.
  • Through the use of DynamicInheritsTable. These tables include all objects that inherit from another object. This inheritance doesn't need to be direct -- for example, even though shrewd baboon doesn't inherit from Creature (it inherits from Baboon), it would still fall under the dynamic inheritance table for Creature since Baboon inherits from BaseApe, which in turn inherits from Humanoid, and which finally inherits from Creature.
  • Through a DynamicSemanticTable. A dynamic semantic table contains all objects that fall into several intersecting categories, such as "medical" and "furniture".

Finally, it's also possible to exclude an object from a dynamic table by using ExcludeFromDynamicEncounters. Check out the Modding:Encounters and Population page for more information on how dynamic tables work.

Mopango pilgrim.png < Because snapjaw mages are descended from the Snapjaw object, which in turn ultimately descends from the Creature object, it turns out that snapjaw mages were already included in some population tables before we started writing PopulationTables.xml! In particular, snapjaw mages could appear in the DynamicInheritsTable:Creatures table, although the probability of them being generated was pretty low.

Returning to our mod: there's a Snapjaws dynamic table that we can add our mages to. Doing so is fairly straightforward: open up Creatures.xml and add the following tag at the end of the definition of Pyovya_SnapjawMage_Fire Mage and Pyovya_SnapjawMage_Ice Mage:

<tag Name="DynamicObjectsTable:Snapjaws" />

One result of adding this tag is that legendary snapjaw mage lairs can now appear! Here's one that I found while traversing the overworld:

Snapjaw Mages -- travelnote fire mage.webp Snapjaw Mages -- legendary fire mage.webp

Conclusion

Congrats! You reached the end of the tutorial!

Mopango pilgrim.png < Woohoo!
Sprouting orb.png < I'm ready to go face these mages in magical combat.

Extra challenge

Before you go, here's a little challenge that you can use to test what you've learned so far. We now have mages of fire and ice; now, try creating a new snapjaw electric mage, along with a tome of electricity that shoots electric projectiles. You're free to go about this however you wish, but here are some general pointers:

  • You'll probably want to give your new mage the Electrical Generation mutation.
  • When you're creating your tome of electricity, start by having it shoot ProjectileElectroPistol; this is the projectile that is shot by an arc winder. Once you're ready, create your own version of this projectile by referring to how ProjectileElectroPistol is implemented in the game's version of ObjectBlueprints/Items.xml.

Next steps

Sprouting orb.png < What should I do if I want to start designing my own creatures?

First off, make sure you read the other Modding pages! They're a treasure trove of valuable information about the various systems that exist in Caves of Qud and how to work with them.

You should definitely spend some time looking at how the game implements different creatures and items. This is the easiest way to learn about existing parts. If you have a good understanding of how a creature behaves in-game, you can look at how they're defined in Creatures.xml and intuit how the parts that have been applied to that creature affect its behavior.

Personally, I also find it helpful to look at other people's mods. There's a section in the Modding:Overview page that includes links to the source code for mods that people have written; it's a valuable reference for figuring out how other people have written mods for Caves of Qud. If you enjoyed making the mod for this tutorial, I'd specifically suggest checking out illuminatiswag's Terrors of the Depths mod (Steam Workshop page), which adds five new monsters to Caves of Qud. In addition to covering the concepts from this tutorial, it also goes a little more in-depth on concepts like C# scripting for creatures.

Finally, feel free to ask for help in the Caves of Qud and Kitfox Discords! We have a steadily-growing community of modders who are all excited to help you out. :)