Modding Preview: The Imperial Cannon

Airships: Conquer the Skies
6 Dec 2015, 4:05 p.m.

(Note that this tutorial is outdated and is only kept for historical interest. See the modding guide for more recent information.)

About a year ago I did a series of blog posts on how to mod a giant cannon into Airships by editing the source code. Now, as a preview for the modding features in dev 8, here's the same thing as a proper mod.

This new modding approach has major advantages: you don't have to know Java, you don't need a Java development setup, the mod will stay compatible when the game updates, and fewer things can go wrong overall.

So in this post I'm going to give you a tour of the structure of the mod. You can download the whole thing from here. Of course you'll have to wait for dev 8 to come out to actually use it.

The mod consists of a directory containing some files and folders:

info.json is the "header" of the mod, containing basic information. It's the presence of an info.json file that makes Airships see the directory as a mod. The file contains the mod's ID, which should be an unique string, as well as its name and description in whatever languages it supports. This one supports English, French and German like the base game, but it's not a requirement for all languages to be present! (Also, you can use mods to add entire new translations to the game, more on that later.)

{
    "id": "imperial_cannon",
    "name": {
        "en": "Imperial Cannon",
        "de": "Kaiserliche Kanone",
        "fr": "Canon Impériale"
    },
    "description": {
        "en": "A powerful but impractically large bronze cannon.\n\nby David Stark",
        "de": "Eine mächtige aber unpraktisch grosse Bronze-Kanone.\n\nvon David Stark",
        "fr": "Une arme massif. Un seul coup peut détruire la plupart des modules.\n\npar David Stark"
    }
}

logo.png is a 300x300 px title image that gets shown in the mod browser.

The images sub-directory contains the spritesheets for the mod. All game graphics are organized into 1024x1024 px spritesheets. The main spritesheet, impcannon.png, consists of the module graphic for the cannon, the barrel, and the giant cannon ball. (Yes, the cannon has a "hood ornament".)

In addition, there is also impcannon_bump.png, which is the normal map for impcannon.png.

Finally, there's impcannon_frags.png, which is a fragment map, indicating what regions the module should break apart into when destroyed.

These images are referenced in sheets.json in the SpritesheetBundle sub-directory. Each data type has its own sub-directory containing one or multiple JSON files with entries. You'll be able to see all the data types and JSON files by looking into Airships' data directory. sheets.json is pretty simple and just ties together the three images as a spritesheet bundle that can then be referenced from elsewhere. (Note that the .png suffix is implicit.)

[
    {
        "name": "impcannon",
        "bump": "impcannon_bump",
        "fragments": "impcannon_frags"
    }
]

Now we get to the meat of the matter: IMPERIAL_CANNON.json in ModuleType. (Module types are named in uppercase for historical/backwards-compatibility reasons.) I won't go over each field in detail, but you can consult the old blog post for an explanation of pretty much all of them.

[
    {
        "name": "IMPERIAL_CANNON",
        "categories": ["WEAPONS"],
        "isWeapon": true,
        "w": 5,
        "h": 3,
        "appearance": {
            "src": "impcannon",
            "x": 0,
            "y": 0,
            "w": 5,
            "h": 3
        },
        "hp": 500,
        "fireHP": 220,
        "moveDelay": 600,
        "weight": 400,
        "cost": 500,
        "crew": 4,
        "recommendedCrew": 5,
        "recommendedGuards": 1,
        "frontOnly": true,
        "isGun": true,
        "isCannon": true,
        "reload": 10000,
        "clip": 1,
        "inaccuracy": 0.0023,
        "blastDmg": 0,
        "penDmg": 400,
        "fireArc": { "direction": "forwards", "degrees": 45 },
        "muzzleCenterX": 3.8,
        "muzzleCenterY": 1.5,
        "muzzleLength": 2.5,
        "optimumRange": 400,
        "fireSound": "hv_cannon",
        "fireSoundCount": 3,
        "explodeHP": 150,
        "explodeDmg": 300,
        "weaponAppearance": {
            "shot": { "src": "impcannon", "x": 97, "y": 1, "w": 13, "h": 13 },
            "barrel": { "src": "impcannon", "x": 9, "y": 55, "w": 80, "h": 34 },
            "barrelX": 20,
            "barrelY": 7,
            "recoil": 15
        },
        "canOccupy": [
            { "x": 0, "y": 2 },
            { "x": 1, "y": 2 },
            { "x": 2, "y": 2 },
            { "x": 3, "y": 2 },
            { "x": 4, "y": 2 },
            { "x": 0, "y": 0 },
            { "x": 0, "y": 1 }
        ],
        "upDoors": [ 0 ],
        "leftDoors": [ 2 ]
    }
]

Finally, the strings directory contains all the translation keys for the mod in English, German and French:

en.properties:

mod_IMPERIAL_CANNON=Imperial Cannon
mod_desc_IMPERIAL_CANNON=A preposterously large weapon. A single hit will destroy most smaller modules.

de.properties:

mod_IMPERIAL_CANNON=Kaiserliche Kanone
mod_desc_IMPERIAL_CANNON=Eine absurd grosse Waffe. Ein einzelner Treffer zerstört die meisten kleineren Module.

fr.properties:

mod_IMPERIAL_CANNON=Canon Impériale
mod_desc_IMPERIAL_CANNON=Une arme massif. Un seul coup peut détruire la plupart des modules.

Finally, there's the generated directory, which contains a whole lot of images automatically derived from the three in images: the spritesheet with various lighting effects baked in, damaged versions, a blueprint version, and the individual fragments of the module. All of this gets created by the mod tooling inside Airships, by simply pressing the "Process Spritesheets" button. So there's no need to go into that folder - just hit "Process Spritesheets" whenever you change anything graphical about your mod during development, and stuff will work.

And that's it! One slightly long JSON file for defining the module itself, some pictures and some really simple additional files to glue things together and provide text, and you have a new module available in-game.