Skip to main content

Advanced Blocks API Usage

Add/remove blocks in one times.

ClipCC provides support for add/remove blocks in one times. You can use api.addBlocks(blocks: BlockPrototype[]) and api.removeBlocks(blocksOpcode: string[]) to implement them.

Filter

ClipCC provides support for filter since version 3.1.4. Here's an example:

api.addBlock({
opcode: 'example.block',
type: type.BlockType.COMMAND,
messageId: 'example.block',
categoryId: 'example.category',
function: () => {...},
option: {
// Only available in stage
filter: type.FilterType.STAGE
}
});

For more block options, see BlockPrototype - BlockOption

ClipCC provides support for menu input since version 3.1.2. The correct way to define a menu's inputs is to define a menu property within parameter.

Static

param: {
PARAMETER: {
type: type.ParameterType.STRING,
menu: [{
messageId: 'example.extension.menu.type',
value: 'type'
}, {
messageId: 'example.extension.menu.rainbow',
value: 'rainbow'
}, {
messageId: 'example.extension.menu.zoom',
value: 'zoom'
}],
default: 'rainbow'
}
}

Dynamic

caution

The following extension APIs are in draft and the following are subject to change in the future.

param: {
PARAMETER: {
type: type.ParameterType.STRING,
menu: () => {
// return sprites
const vm = api.getVmInstance();
const sprites = [];
for (const targetId in vm.runtime.targets) {
if (!vm.runtime.targets.hasOwnProperty(targetId)) continue;
const name = vm.runtime.targets[targetId].sprite.name;
sprites.push([name, name]);
}
return sprites;
},
default: 'rainbow'
}
}

If you want to define a menu that cannot embed reporters, then set the field property to true.

param: {
PARAMETER: {
type: type.ParameterType.STRING,
default: 'something',
field: true,
menu: [...]
}
},

Hat

There's a simple example illustrating how to define a block of type HAT:

api.addBlock({
opcode: 'example.hat',
type: type.BlockType.HAT,
messageId: 'example.hat',
categoryId: 'example.category',
param: {
CONDITION: {
type: type.ParameterType.BOOLEAN
}
},
function: (args) => {
if (!!hasReported) {
window.hasReported = false;
return false;
}
const result = !!args.CONDITION;
if (result) {
hasReported = true;
return true;
}
return false;
}
});

When the block is dragged into the workspace, the entire project will be under the illusion of "unstoppable" but no scripts are being executed. The editor checks every frame to see if this HAT is triggerred. The hat block will only be called normally if the previous frame returns false and the current frame returns true.

Branch

ClipCC provides support for menu input since version 3.1.4. Here's the definition:

index.js
api.addBlock({
opcode: 'example.if',
type: type.BlockType.COMMAND,
messageId: 'example.if',
categoryId: 'example.category',
branchCount: 1,
param: {
COND: {
type: type.ParameterType.BOOLEAN
}
},
function: (args, util) => {
// If the condition is true, start the branch.
if (!!args.COND) util.startBranch(1, false);
}
});
en.json
{
"example.if": "if [COND] [SUBSTACK]"
}

  You should specify "branchCount" in BlockPrototype, which means the number of branches of the block. You should also specify the location of the branch in the translation file and name it with [SUBSTACKX].   For a BRANCH block, You can control the flow through the startBranch provided in the util object.

/**
* Start a branch in the current block.
* @param {number} branchNum Which branch to step to (i.e., 1, 2).
* @param {boolean} isLoop Whether this block is a loop.
*/
startBranch (branchNum, isLoop) {...}

When the isLoop parameter is specified as true, the blocks will be executed repeatedly until no startBranch is triggered. When false, the block will be ejected immediately. When the branchNum parameter is not specified, it defaults to 1. There's an example.

Writing this type of block usually requires modifications to the thread and sequencer, so you need to have a certain level of understanding of the Scratch/ClipCC source code. In general, we do not recommend that you create such blocks.