Skip to main content

Annotating blocks for Catnip language

March 29, 2024

Annotating blocks for Catnip language

Ct.js checks for two things when adding blocks for your catmod:

Both files are optional. You can use one, or both, to create blocks for Catnip.

All the discovered blocks will be put in a category special for your catmod, but some blocks can additionally be put into built-in categories by their names (supported in blocks.js file only atm).

Adding @catnip annotations to types.d.ts

You can add several catnip annotations to help ct.js better convert your declaration file to usable catnip blocks. They are added as special tags in JSDoc comments. For example:

/**
 * Creates a new copy of a given template inside a specific room.
 * @param template The name of the template to use
 * @catnipAsset template:template
 * @param [x] The x coordinate of a new copy. Defaults to 0.
 * @param [y] The y coordinate of a new copy. Defaults to 0.
 * @param [room] The room to which add the copy.
 * Defaults to the current room.
 * @param [params] An optional object which parameters will be applied
 * to the copy prior to its OnCreate event.
 * @returns The created copy.
 * @catnipSaveReturn
 */
function copyIntoRoom(template: string, x = 0, y = 0, room: Room, params: Record<string, unknown> = {}): BasicCopy;

Properties are automatically turned into computed blocks. (The sausage-shaped ones.) By default, functions that do not return a value turn into command blocks (rectangular ones), and those that do return a value turn into computed blocks. But you can overwrite this behavior — see below.

Ct.js recognises these tags:

Adding blocks through blocks.js file

blocks.js must be a CommonJS module (meaning that it must return a value with module.exports). The file must return an array of blocks you wish to add to the Catnip library.

For example, here is a blocks.js file from the place catmod:

module.exports = [{
    name: 'Move this copy along a line stopping at',
    name_Ru: 'Переместить эту копию по линии, останавливаясь перед',
    type: 'command',
    code: 'move template bullet',
    icon: 'move',
    category: 'Movement',
    pieces: [{
        type: 'argument',
        key: 'cgroup',
        typeHint: 'string',
        required: true
    }, {
        type: 'filler'
    }, {
        type: 'label',
        name: 'store in',
        i18nKey: 'store in'
    }, {
        type: 'argument',
        key: 'return',
        typeHint: 'wildcard'
    }],
    jsTemplate: (values) => {
        if (values.return !== 'undefined') {
            return `${values.return} = this.moveBullet(${values.cgroup}, ${values.precision || 1});`;
        }
        return `this.moveBullet(${values.cgroup}, ${values.precision || 1});`;
    }
}, {
    name: 'Move this copy stopping at',
    name_Ru: 'Переместить эту копию, останавливаясь перед',
    type: 'command',
    code: 'move template smart',
    icon: 'move',
    category: 'Movement',
    pieces: [{
        type: 'argument',
        key: 'cgroup',
        typeHint: 'string',
        required: true
    }, {
        type: 'filler'
    }, {
        type: 'label',
        name: 'store in',
        i18nKey: 'store in'
    }, {
        type: 'argument',
        key: 'return',
        typeHint: 'wildcard'
    }],
    jsTemplate: (values) => {
        if (values.return !== 'undefined') {
            return `${values.return} = this.moveSmart(${values.cgroup}, ${values.precision || 1});`;
        }
        return `this.moveSmart(${values.cgroup}, ${values.precision || 1});`;
    }
}];

Fields name, type, code, jsTemplate, and pieces are required.

Any detected errors will be logged into ct.IDE's devtools (accessible from Main menu — Troubleshooting) console when you open a project or enable a new catmod.

Translating blocks

You may be able to reuse already used keys. Available translation keys can be found in ct.js folder — data — i18n — English.json file — catnip object.

If you can't reuse existing keys, you can add translations for name and displayName by adding a similar key with an underscore and a region code: for example, name_Ru will be a name used for Russian language.

Augmenting built-in categories

You can add a field category for a block and write a name of the category you want this block to be added to.

Built-in category names are:

Adding more pieces (icons, arguments) to a block

Anything that is not a block's icon or name is defined in pieces array of a block. Each piece can be one of these interfaces:

declare interface IBlockPieceLabel {
    type: 'label';
    name: string;
    i18nKey?: string;
}
declare interface IBlockPieceIcon {
    type: 'icon';
    icon: string;
}
declare interface IBlockPieceCode {
    type: 'code';
    key: string;
}
declare interface IBlockPieceArgument { // A chip for a constant value or a reference to an asset
    type: 'argument';
    key: string;
    typeHint: blockArgumentType;
    defaultConstant?: string;
    required?: boolean;
    assets?: resourceType | 'action';
}
declare interface IBlockPieceTextbox {
    type: 'textbox';
    key: string;
    default?: string;
}
declare interface IBlockPieceBlocks { // A block area.
    type: 'blocks';
    placeholder?: 'do nothing';
    key: string;
}
declare interface IBlockPieceBreak { // A line break
    type: 'break'
}
declare interface IBlockFiller { // Moves next blocks to the right
    type: 'filler'
}
declare interface IBlockAsyncMarker { // Adds an Async icon
    type: 'asyncMarker'
}

blockArgumentType is 'boolean' | 'number' | 'string' | 'wildcard' | 'void', but 'void' is reserved for internal use and should not be used in custom blocks.

Emitting JS code

Each block must have a field jsTemplate that must be a function. This template is passed with a values argument that lets you read all the arguments and precompiled code of block lists. You do not need to escape or quote strings — use them as is as compiler already does that for you.

Blocks must return a string that contains a valid JS script or expression.

When an argument is empty, unless a defaultConstant is set, the argument is set to "undefined". Note that this is a string. When a block list is empty, its value is an empty string. ("")

Setting an icon for block category

You can add a field "icon" to your main section of the module.json file and set it to a string — to a name of one of the icons that are listed in ct.js main menu — Meta — List of icons.