const BlockType = require('../../extension-support/block-type') const BlockShape = require('../../extension-support/block-shape') const ArgumentType = require('../../extension-support/argument-type') const TargetType = require('../../extension-support/target-type') const Cast = require('../../util/cast') function span(text) { let el = document.createElement('span') el.innerHTML = text el.style.display = 'hidden' el.style.whiteSpace = 'nowrap' el.style.width = '100%' el.style.textAlign = 'center' return el } class jwTargetType { customId = "jwTargets" targetId = "" constructor(targetId) { this.targetId = targetId } static toTarget(x) { if (x instanceof jwTargetType) return x if (typeof x == "string") return new jwTargetType(x) return new jwTargetType("") } jwArrayHandler() { try { return `Target<${this.target.sprite.name}>` } catch { return `Target` } } toString() { return this.targetId } toMonitorContent = () => span(this.toString()) toReporterContent() { try { let target = this.target let name = target.sprite.name let isClone = !target.isOriginal let costumeURI = target.getCostumes()[target.currentCostume].asset.encodeDataURI() let root = document.createElement('div') root.style.display = 'flex' root.style.flexDirection = 'column' root.style.justifyContent = 'center' let img = document.createElement('img') img.src = costumeURI img.style.maxWidth = '150px' img.style.maxHeight = '150px' root.appendChild(img) root.appendChild(span(`${name}${isClone ? ' (clone)' : ''}`)) return root } catch { return span("Unknown") } } get target() { return vm.runtime.getTargetById(this.targetId) } } const Target = { Type: jwTargetType, Block: { blockType: BlockType.REPORTER, forceOutputType: "Target", disableMonitor: true }, Argument: { check: ["Target"] } } let jwArray = { Type: class {}, Block: {}, Argument: {} } class Extension { constructor() { vm.jwTargets = Target vm.runtime.registerSerializer( "jwTargets", v => v.targetId, v => new Target.Type(v) ); if (!vm.jwArray) vm.extensionManager.loadExtensionIdSync('jwArray') jwArray = vm.jwArray } getInfo() { return { id: "jwTargets", name: "Targets", color1: "#4254f5", menuIconURI: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM6Yng9Imh0dHBzOi8vYm94eS1zdmcuY29tIj4KICA8Y2lyY2xlIHN0eWxlPSJzdHJva2Utd2lkdGg6IDJweDsgcGFpbnQtb3JkZXI6IHN0cm9rZTsgZmlsbDogcmdiKDY2LCA4NCwgMjQ1KTsgc3Ryb2tlOiByZ2IoNDAsIDU1LCAxOTkpOyIgY3g9IjEwIiBjeT0iMTAiIHI9IjkiPjwvY2lyY2xlPgogIDxwYXRoIGQ9Ik0gMTAgMy4yMjIgQyA0Ljc4MyAzLjIyMiAxLjUyMyA4Ljg3IDQuMTMgMTMuMzg5IEMgNS4zNCAxNS40ODUgNy41OCAxNi43NzggMTAgMTYuNzc4IEMgMTUuMjE4IDE2Ljc3OCAxOC40OCAxMS4xMyAxNS44NjkgNi42MTEgQyAxNC42NjEgNC41MTUgMTIuNDIyIDMuMjIyIDEwIDMuMjIyIE0gMTAgNS40ODEgQyAxMy40NzkgNS40ODEgMTUuNjUzIDkuMjQ4IDEzLjkxMyAxMi4yNTkgQyAxMy4xMDYgMTMuNjU4IDExLjYxNiAxNC41MTkgMTAgMTQuNTE5IEMgNi41MjIgMTQuNTE5IDQuMzUgMTAuNzUyIDYuMDg3IDcuNzQxIEMgNi44OTUgNi4zNDIgOC4zODUgNS40ODEgMTAgNS40ODEgTSAxMCA3Ljc0MSBDIDguMjYyIDcuNzQxIDcuMTczIDkuNjIyIDguMDQ0IDExLjEzIEMgOC40NDggMTEuODI4IDkuMTkzIDEyLjI1OSAxMCAxMi4yNTkgQyAxMS43NCAxMi4yNTkgMTIuODI3IDEwLjM3OCAxMS45NTYgOC44NyBDIDExLjU1MyA4LjE3MiAxMC44MDggNy43NDEgMTAgNy43NDEiIGZpbGw9IiNmZmYiIHN0eWxlPSIiPjwvcGF0aD4KPC9zdmc+", blocks: [ { opcode: 'this', text: 'this target', hideFromPalette: true, ...Target.Block }, { opcode: 'stage', text: 'stage target', hideFromPalette: true, ...Target.Block }, { opcode: 'fromName', text: '[SPRITE] target', arguments: { SPRITE: { menu: "sprite" } }, ...Target.Block }, { opcode: 'cloneOrigin', text: 'origin of [TARGET]', arguments: { TARGET: Target.Argument }, ...Target.Block }, '---', { opcode: 'get', text: '[TARGET] [MENU]', blockType: BlockType.REPORTER, arguments: { TARGET: Target.Argument, MENU: { menu: "targetProperty", defaultValue: "name" } } }, { opcode: 'set', text: 'set [TARGET] [MENU] to [VALUE]', blockType: BlockType.COMMAND, arguments: { TARGET: Target.Argument, MENU: { menu: "targetPropertySet", defaultValue: "x" }, VALUE: { type: ArgumentType.STRING, exemptFromNormalization: true } } }, '---', { opcode: 'isClone', text: 'is [TARGET] a clone', blockType: BlockType.BOOLEAN, arguments: { TARGET: Target.Argument } }, { opcode: 'isTouching', text: 'is [A] touching [B]', blockType: BlockType.BOOLEAN, arguments: { A: Target.Argument, B: Target.Argument } }, '---', { opcode: 'getVar', text: 'var [NAME] of [TARGET]', blockType: BlockType.REPORTER, allowDropAnywhere: true, arguments: { TARGET: Target.Argument, NAME: { type: ArgumentType.STRING } } }, { opcode: 'setVar', text: 'set var [NAME] of [TARGET] to [VALUE]', blockType: BlockType.COMMAND, arguments: { TARGET: Target.Argument, NAME: { type: ArgumentType.STRING }, VALUE: { type: ArgumentType.STRING, exemptFromNormalization: true } } }, '---', { opcode: 'clone', text: 'create clone of [TARGET]', blockType: BlockType.COMMAND, arguments: { TARGET: Target.Argument } }, { opcode: 'cloneR', text: 'create clone of [TARGET]', arguments: { TARGET: Target.Argument }, ...Target.Block }, '---', { opcode: 'all', text: 'all targets', ...jwArray.Block }, { opcode: 'touching', text: 'targets touching [TARGET]', arguments: { TARGET: Target.Argument }, ...jwArray.Block }, { opcode: 'clones', text: 'clones of [TARGET]', arguments: { TARGET: Target.Argument }, ...jwArray.Block }, { opcode: 'arrayHasTarget', text: '[ARRAY] has clone of [TARGET]', blockType: BlockType.BOOLEAN, arguments: { ARRAY: jwArray.Argument, TARGET: Target.Argument } }, '---', { blockType: BlockType.XML, xml: `` } ], menus: { sprite: { acceptReporters: true, items: 'getSpriteMenu' }, targetProperty: { acceptReporters: true, items: [ "name", "x", "y", "direction", "size", "stretch x", "stretch y", "costume #", "costume name", ] }, targetPropertySet: { acceptReporters: true, items: [ "x", "y", "direction", "size", "stretch x", "stretch y", "costume #", "costume name", ] } } }; } getSpriteMenu({}) { let sprites = ["this", "stage"] for (let target of vm.runtime.targets.filter(v => v !== vm.runtime._stageTarget)) { if (!sprites.includes(target.sprite.name)) sprites.push(target.sprite.name) } return sprites } this({}, util) { return new Target.Type(util.target.id) } stage() { return new Target.Type(vm.runtime._stageTarget.id) } fromName({SPRITE}, util) { SPRITE = Cast.toString(SPRITE) if (SPRITE == "this") return this.this({}, util) if (SPRITE == "stage") return this.stage() let target = vm.runtime.getSpriteTargetByName(SPRITE) return new Target.Type(target ? target.id : "") } cloneOrigin({TARGET}, util) { TARGET = Target.Type.toTarget(TARGET) if (!TARGET.target) return "" return this.fromName({SPRITE: TARGET.target.sprite.name}, util) } get({TARGET, MENU}) { TARGET = Target.Type.toTarget(TARGET) MENU = Cast.toString(MENU) if (!TARGET.target) return "" switch(MENU) { case "x": return TARGET.target.x case "y": return TARGET.target.y case "direction": return TARGET.target.direction case "size": return TARGET.target.size case "name": return TARGET.target.sprite.name case "stretch x": return TARGET.target.stretch[0] case "stretch y": return TARGET.target.stretch[1] case "costume #": return TARGET.target.currentCostume + 1 case "costume name": return TARGET.target.getCurrentCostume().name } return "" } set({TARGET, MENU, VALUE}) { TARGET = Target.Type.toTarget(TARGET) MENU = Cast.toString(MENU) if (!TARGET.target) return switch(MENU) { case "x": TARGET.target.setXY(Cast.toNumber(VALUE), TARGET.target.y) break case "y": TARGET.target.setXY(TARGET.target.x, Cast.toNumber(VALUE)) break case "direction": TARGET.target.setDirection(Cast.toNumber(VALUE)) break case "size": TARGET.target.setSize(Cast.toNumber(VALUE)) break case "stretch x": TARGET.target.setStretch(Cast.toNumber(VALUE), TARGET.target.stretch[1]) break case "stretch y": TARGET.target.setStretch(TARGET.target.stretch[0], Cast.toNumber(VALUE)) break case "costume #": TARGET.target.setCostume(Cast.toNumber(VALUE) - 1) break case "costume name": let index = TARGET.target.getCostumes().indexOf(TARGET.target.getCostumes().find(v => v.name === Cast.toString(VALUE))) TARGET.target.setCostume(index) break } } isClone({TARGET}) { TARGET = Target.Type.toTarget(TARGET) if (!TARGET.target) return false return !TARGET.target.isOriginal } isTouching({A, B}) { A = Target.Type.toTarget(A) B = Target.Type.toTarget(B) if (!A.target) return return A.target.isTouchingTarget(B.targetId) } getVar({TARGET, NAME}) { TARGET = Target.Type.toTarget(TARGET) NAME = Cast.toString(NAME) if (!TARGET.target) return "" let variable = Object.values(TARGET.target.variables).find(v => v.name == NAME) if (!variable) return "" return variable.value } setVar({TARGET, NAME, VALUE}) { TARGET = Target.Type.toTarget(TARGET) NAME = Cast.toString(NAME) if (!TARGET.target) return let variable = Object.values(TARGET.target.variables).find(v => v.name == NAME) if (!variable) return variable.value = VALUE } clone(args) { this.cloneR(args) } cloneR({TARGET}) { TARGET = Target.Type.toTarget(TARGET) if (!TARGET.target) return let origin = TARGET.target let clone = origin.makeClone() if (clone) { vm.runtime.addTarget(clone) clone.goBehindOther(origin) //mimick clone making from control category } return new Target.Type(clone ? clone.id : "") } all() { return new jwArray.Type(vm.runtime.targets.map(v => new Target.Type(v.id))) } touching({TARGET}) { TARGET = Target.Type.toTarget(TARGET) if (!TARGET.target) return new jwArray.Type let targets = vm.runtime.targets targets = targets.filter(v => v !== TARGET && !v.isStage) targets = targets.filter(v => v.isTouchingTarget(TARGET.targetId)) return new jwArray.Type(targets.map(v => new Target.Type(v.id))) } clones({TARGET}) { TARGET = Target.Type.toTarget(TARGET) if (TARGET.target) { return new jwArray.Type(TARGET.target.sprite.clones.filter(v => !v.isOriginal).map(v => new Target.Type(v.id))) } return new jwArray.Type() } arrayHasTarget({ARRAY, TARGET}) { ARRAY = jwArray.Type.toArray(ARRAY) TARGET = Target.Type.toTarget(TARGET) if (!TARGET.target) return false return ARRAY.array.find(v => { let target = Target.Type.toTarget(v) if (!target.target) return false return target.target.sprite == TARGET.target.sprite }) !== undefined } } module.exports = Extension