const formatMessage = require('format-message'); const BlockType = require('../../extension-support/block-type'); const ArgumentType = require('../../extension-support/argument-type'); // const Cast = require('../../util/cast'); /** * Class for CloneTool blocks * @constructor */ class JgCloneToolBlocks { constructor(runtime) { /** * The runtime instantiating this block package. * @type {Runtime} */ this.runtime = runtime; } /** * @returns {object} metadata for this extension and its blocks. */ getInfo() { return { id: 'jgClones', name: 'Clone Communication', color1: '#FFAB19', color2: '#EC9C13', blocks: [ { blockType: BlockType.LABEL, text: "Main Sprite Communication" }, { opcode: 'getCloneWithVariableSetTo', text: formatMessage({ id: 'jgClones.blocks.getCloneWithVariableSetTo', default: 'get [DATA] of clone with [VAR] set to [VALUE]', description: 'Block that returns the value of the item picked within a clone with a variable set to a certain value.' }), disableMonitor: true, blockType: BlockType.REPORTER, arguments: { DATA: { type: ArgumentType.STRING, menu: 'spriteData' }, VAR: { type: ArgumentType.STRING, menu: 'spriteVariables' }, VALUE: { type: ArgumentType.STRING, defaultValue: '0' }, } }, { opcode: 'getCloneVariableWithVariableSetTo', text: formatMessage({ id: 'jgClones.blocks.getCloneVariableWithVariableSetTo', default: 'get [VAR1] of clone with [VAR2] set to [VALUE]', description: 'Block that returns the value of the variable picked within a clone with a variable set to a certain value.' }), disableMonitor: true, blockType: BlockType.REPORTER, arguments: { VAR1: { type: ArgumentType.STRING, menu: 'spriteVariables' }, VAR2: { type: ArgumentType.STRING, menu: 'spriteVariables' }, VALUE: { type: ArgumentType.STRING, defaultValue: '0' }, } }, // { // opcode: 'setValueOnCloneWithVariableSetTo', // text: formatMessage({ // id: 'jgClones.blocks.setValueOnCloneWithVariableSetTo', // default: 'set [DATA] to [VALUE1] on clone with [VAR] set to [VALUE2]', // description: 'Block that sets the value of the item picked within a clone with a variable set to a certain value.' // }), // blockType: BlockType.COMMAND, // arguments: { // DATA: { type: ArgumentType.STRING, menu: 'spriteData' }, // VALUE1: { type: ArgumentType.STRING, defaultValue: '0' }, // VAR: { type: ArgumentType.STRING, menu: 'spriteVariables' }, // VALUE2: { type: ArgumentType.STRING, defaultValue: '0' }, // } // }, { opcode: 'setVariableOnCloneWithVariableSetTo', text: formatMessage({ id: 'jgClones.blocks.setVariableOnCloneWithVariableSetTo', default: 'set [VAR1] to [VALUE1] on clone with [VAR2] set to [VALUE2]', description: 'Block that sets a variable within a clone with a variable set to a certain value.' }), blockType: BlockType.COMMAND, arguments: { VAR1: { type: ArgumentType.STRING, menu: 'spriteVariables' }, VALUE1: { type: ArgumentType.STRING, defaultValue: '0' }, VAR2: { type: ArgumentType.STRING, menu: 'spriteVariables' }, VALUE2: { type: ArgumentType.STRING, defaultValue: '0' }, } }, "---", "---", { blockType: BlockType.LABEL, text: "Clone Communication" }, { opcode: 'getMainSpriteData', text: formatMessage({ id: 'jgClones.blocks.getMainSpriteData', default: 'get [DATA] of main sprite', description: 'Block that returns the value of the item picked on the main sprite.' }), disableMonitor: true, blockType: BlockType.REPORTER, arguments: { DATA: { type: ArgumentType.STRING, menu: 'spriteData' } } }, { opcode: 'getVariableOnMainSprite', text: formatMessage({ id: 'jgClones.blocks.getVariableOnMainSprite', default: 'get [VAR] of main sprite', description: 'Block that returns the value of the variable picked on the main sprite.' }), disableMonitor: true, blockType: BlockType.REPORTER, arguments: { VAR: { type: ArgumentType.STRING, menu: 'spriteVariables' }, } }, // { // opcode: 'setValueOnMainSprite', // text: formatMessage({ // id: 'jgClones.blocks.setValueOnMainSprite', // default: 'set [DATA] to [VALUE] on main sprite', // description: 'Block that sets the value of the item picked within the main sprite.' // }), // blockType: BlockType.COMMAND, // arguments: { // DATA: { type: ArgumentType.STRING, menu: 'spriteData' }, // VALUE: { type: ArgumentType.STRING, defaultValue: '0' } // } // }, { opcode: 'setVariableOnMainSprite', text: formatMessage({ id: 'jgClones.blocks.setVariableOnMainSprite', default: 'set [VAR] to [VALUE] on main sprite', description: 'Block that sets a variable within the main sprite.' }), blockType: BlockType.COMMAND, arguments: { VAR: { type: ArgumentType.STRING, menu: 'spriteVariables' }, VALUE: { type: ArgumentType.STRING, defaultValue: '0' } } }, "---", "---", { blockType: BlockType.LABEL, text: "Other" }, { opcode: 'getIsClone', text: formatMessage({ id: 'jgClones.blocks.getIsClone', default: 'is clone?', description: 'Block that returns whether the current sprite is a clone or not.' }), disableMonitor: true, blockType: BlockType.BOOLEAN }, { opcode: 'clonesInSprite', text: formatMessage({ id: 'jgClones.blocks.clonesInSprite', default: 'clone count of [SPRITE]', description: 'Block that returns the amount of clones of this sprite that currently exist.' }), disableMonitor: true, blockType: BlockType.REPORTER, arguments: { SPRITE: { type: ArgumentType.STRING, menu: 'sprites' }, } } ], menus: { sprites: "getSpriteMenu", spriteVariables: "getSpriteVariablesMenu", spriteData: { acceptReporters: true, items: [ // motion "x position", "y position", "direction", "rotation style", // looks (excluding effects) "visible", "costume number", "costume name", "size", "x stretch", "y stretch", // sound "volume", // sensing "draggable", // music (doesnt seem to work) // "tempo", // effects "color effect", "fisheye effect", "whirl effect", "pixelate effect", "mosaic effect", "brightness effect", "ghost effect", "saturation effect", "red effect", "green effect", "blue effect", "opaque effect", ].map(item => ({ text: item, value: item })) } } }; } // utilities getClones (sprite) { // i call this the stair step null check if (!sprite.clones) return []; const clones = sprite.clones return clones.filter(clone => clone.isOriginal === false); } getTargetBySpriteName (name) { const targets = this.runtime.targets; const sprites = targets.filter(target => target.isOriginal).filter(target => target.sprite.name == name); return sprites[0]; } getTargetClonesByVariableSetToValue (target, variableName, value) { const clones = this.getClones(target.sprite); const cloneList = clones.filter(clone => { const variables = Object.getOwnPropertyNames(clone.variables).map(variableId => { return clone.variables[variableId]; }); const variable = variables.filter(variable => variable.name == variableName)[0]; if (!variable) return false; if (String(variable.value) != String(value)) return false; return true; }); return cloneList; } getTargetCloneByVariableSetToValue (target, variableName, value) { const clones = this.getTargetClonesByVariableSetToValue(target, variableName, value); if (!clones) return; return clones[0]; } menuOptionToTargetProperty (option) { switch (option) { case "x position": return "x"; case "y position": return "y"; case "x stretch": return "xStretch"; case "y stretch": return "yStretch"; case "rotation style": return "rotationStyle"; case "costume number": return "currentCostume"; case "costume name": return "costumeName"; default: return option; } } getMainSprite (target) { if (!target.sprite) return; const clones = target.sprite.clones; const mainSprites = clones.filter(clone => clone.isOriginal); return mainSprites[0]; } // menus getSpriteMenu () { const targets = this.runtime.targets; const emptyMenu = [{ text: "", value: "" }]; if (!targets) return emptyMenu; const menu = targets.filter(target => target.isOriginal && (!target.isStage)).map(target => ({ text: target.sprite.name, value: target.sprite.name })); return (menu.length > 0) ? menu : emptyMenu; } getSpriteVariablesMenu () { const target = vm.editingTarget; const emptyMenu = [{ text: "", value: "" }]; if (!target) return emptyMenu; if (!target.variables) return emptyMenu; const menu = Object.getOwnPropertyNames(target.variables).map(variableId => { const variable = target.variables[variableId] return { text: variable.name, value: variable.name, } }); // check if menu has 0 items because pm throws an error if theres no items return (menu.length > 0) ? menu : emptyMenu; } // blocks // other blocks getIsClone (_, util) { return !util.target.isOriginal; } clonesInSprite (args) { const target = this.getTargetBySpriteName(args.SPRITE); if (!target) return 0; const clones = this.getClones(target.sprite); return clones.length; } // main sprite communication getCloneWithVariableSetTo (args, util) { const target = util.target; const clone = this.getTargetCloneByVariableSetToValue(target, args.VAR, String(args.VALUE)); if (!clone) return ""; const property = this.menuOptionToTargetProperty(args.DATA); switch (property) { case "currentCostume": return clone.currentCostume + 1; case "costumeName": return clone.sprite.costumes_[clone.currentCostume].name; case "xStretch": return clone.stretch[0]; case "yStretch": return clone.stretch[1]; case "color effect": return clone.effects.color; case "fisheye effect": return clone.effects.fisheye; case "whirl effect": return clone.effects.whirl; case "pixelate effect": return clone.effects.pixelate; case "mosaic effect": return clone.effects.mosaic; case "brightness effect": return clone.effects.brightness; case "ghost effect": return clone.effects.ghost; case "red effect": return clone.effects.red; case "green effect": return clone.effects.green; case "blue effect": return clone.effects.blue; case "opaque effect": return clone.effects.opaque; case "saturation effect": return clone.effects.saturation; default: return clone[property]; } } getCloneVariableWithVariableSetTo (args, util) { const target = util.target; const clone = this.getTargetCloneByVariableSetToValue(target, args.VAR2, String(args.VALUE)); if (!clone) return ""; const variables = {}; Object.getOwnPropertyNames(clone.variables).forEach(id => { variables[clone.variables[id].name] = clone.variables[id].value; }); return variables[args.VAR1]; } setVariableOnCloneWithVariableSetTo (args, util) { const target = util.target; const clones = this.getTargetClonesByVariableSetToValue(target, args.VAR2, String(args.VALUE2)); clones.forEach(clone => { Object.getOwnPropertyNames(clone.variables).forEach(variableId => { const variable = clone.variables[variableId]; if (variable.name !== args.VAR1) return; const value = isNaN(Number(args.VALUE1)) ? String(args.VALUE1) : Number(args.VALUE1); variable.value = value; }); }); } // clone communication getMainSpriteData (args, util) { const target = util.target; const mainSprite = this.getMainSprite(target); if (!mainSprite) return ""; const property = this.menuOptionToTargetProperty(args.DATA); switch (property) { case "currentCostume": return mainSprite.currentCostume + 1; case "costumeName": return mainSprite.sprite.costumes_[mainSprite.currentCostume].name; case "xStretch": return mainSprite.stretch[0]; case "yStretch": return mainSprite.stretch[1]; case "color effect": return mainSprite.effects.color; case "fisheye effect": return mainSprite.effects.fisheye; case "whirl effect": return mainSprite.effects.whirl; case "pixelate effect": return mainSprite.effects.pixelate; case "mosaic effect": return mainSprite.effects.mosaic; case "brightness effect": return mainSprite.effects.brightness; case "ghost effect": return mainSprite.effects.ghost; case "red effect": return mainSprite.effects.red; case "green effect": return mainSprite.effects.green; case "blue effect": return mainSprite.effects.blue; case "opaque effect": return mainSprite.effects.opaque; case "saturation effect": return mainSprite.effects.saturation; default: return mainSprite[property]; } } getVariableOnMainSprite(args, util) { const target = util.target; const mainSprite = this.getMainSprite(target); if (!mainSprite) return ""; const variables = {}; Object.getOwnPropertyNames(mainSprite.variables).forEach(id => { variables[mainSprite.variables[id].name] = mainSprite.variables[id].value; }); return variables[args.VAR]; } setVariableOnMainSprite (args, util) { const target = util.target; const mainSprite = this.getMainSprite(target); Object.getOwnPropertyNames(mainSprite.variables).forEach(variableId => { const variable = mainSprite.variables[variableId]; if (variable.name !== args.VAR) return; const value = isNaN(Number(args.VALUE)) ? String(args.VALUE) : Number(args.VALUE); variable.value = value; }); } } module.exports = JgCloneToolBlocks;