您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
adds new functionality to scratch.mit.edu
// ==UserScript== // @name scratch extended // @version 0.18 // @description adds new functionality to scratch.mit.edu // @run-at document-start // @author rssaromeo // @match *://scratch.mit.edu/* // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAkFJREFUeF7l2sFRAzEMBVBSBww3qqAP6qAUiqEFCuDMjQl1wPhgxuw4kS39r28P3JgkjvRsybvenJ5e379v/vHfKRPg6+O5S3378CKbgjSAS8nXzFUIKQBt8ue3zz+zffd4//u/AoEOcC35mrkSgQowkrwagQYwk7wSgQLgSV6FAAeIJK9AgAKMJl+b3nFHaLeHrMYIA5hNviarRoAAeJNfASEM4Em+XvBEPou6dg4BIBJAjBHBcAMgA0eONYvhAmAEzBhzBCMEgO7gCgQKQGQPz0ZYDqAs21mEyG00BaAkEVkFowj1O2QAJVB0H2gbl7USZACjM8ReCVIAFUJva0svgZllirrFZZ0ou5rgcRasWkUhjFzYzL4HApBZDrMJWu+HAeyKAAXYEQEOsBsCBWAnBBrALghUgB0Q6ACrI6QArIyQBrAqQirAigjpAKshSABWQpABrIIgBVgBwQ2APKBQnie4ABg/eVMhhADQP3lTIEAB2KfA7elO9LlDHQsOoEBIPxWuS1X5UKSFlgGUIFgII/1A+mBkJEBvOcyOnb4CagOZDbR8zgqWMea1o3FXE2wHRAaMHMt6HhDaBY6DIwJHjDGadPu+8ApAlIMq+RI7DMB7Y9POBmtHofYARDkwt1OrLKArwFsOipmHNsGe8mhdX5sh1PV+agl4tshegBnJw5sgaiVkJZ8CMLM7eC+drUYnK4HZcsiceXoTnC0HRfJpJXBpJfSQrJulyHLvfZZyHWAFiTxRtr7Lel0CYAWV+foPdui0399khioAAAAASUVORK5CYII= // @grant none // @tag unused // @license GPLv3 // @namespace https://greasyfork.org/users/1184528 // ==/UserScript== window.getvm = getvm function getvm() { return new Promise((resolve) => { var c if (window.vm) c = test(true) if (c) resolve(c) new Promise(() => { const oldBind = Function.prototype.bind Function.prototype.bind = function (...args) { if ( args[0] && args[0].hasOwnProperty("editingTarget") && args[0].hasOwnProperty("runtime") ) { Function.prototype.bind = oldBind window.vm = args[0] test() // vm.runtime._primitives.operator_gt = (e, y) => { // alert("got it") // } } return oldBind.apply(this, args) } }) function test(once) { if ( document?.querySelectorAll("canvas") && (window?.vm || document ?.querySelector("#app") ?._reactRootContainer?._internalRoot?.current?.child?.stateNode?.store?.getState?.() ?.scratchGui?.vm) && (window?.vm || (document ?.querySelector("#app") ?._reactRootContainer?._internalRoot?.current?.child?.stateNode?.store?.getState?.() ?.scratchGui?.vm?.runtime?._lastStepDoneThreads?.length && window.vm.runtime.getTargetForStage())) ) { window.vm = window?.vm || document .querySelector("#app") ._reactRootContainer._internalRoot.current.child.stateNode.store.getState() .scratchGui.vm if (once) return window.vm resolve(window.vm) } else { if (once) return false setTimeout(test, 1) } } }) } getvm().then((vm) => { window.vm = vm vm.runtime._primitives.operator_gt = (...e) => { var c = w(...e) return c } var b = setInterval(() => { if (canvas() && vm.runtime.getTargetForStage()) { clearInterval(b) loaded() } }) }) var w window.canvas = canvas function loaded() { function gtgotran(e, y) { datatype = "gt" var func = e.OPERAND1 var args = String(e.OPERAND2) if ( /^[$!#%()*+,-./:;=?@\[\]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789]{20}$/.test( args, ) ) args = Object.values(y?.thread?.target?.comments)?.filter?.( (e) => e.id == args, )?.[0]?.text ?? args if (func === "isloaded") { scratchvar("__script loaded", "true") return true } window.__scratchextended.thread = y?.thread window.__scratchextended.eventdata = y?.thread?.target if (window.__scratchextendendebug) console.log(y?.thread?.target?.sprite?.name) if (func === "") { return (y.thread.lastvalue = parse(args).join("")) } else if (__scratchextended[func]) { return (y.thread.lastvalue = run(func, parse(args, func !== "escape")) || "") } else { return Number(e.OPERAND1) > Number(e.OPERAND2) } } w = gtgotran var mousestate = { left: false, center: false, right: false, } function mousechanged(e) { e.preventDefault() switch (e.buttons) { case 0: mousestate = { left: false, center: false, right: false, } break case 1: mousestate = { left: true, center: false, right: false, } break case 2: mousestate = { left: false, center: false, right: true, } break case 3: mousestate = { left: true, center: false, right: true, } break case 4: mousestate = { left: false, center: true, right: false, } break case 5: mousestate = { left: true, center: true, right: false, } break case 6: mousestate = { left: false, center: true, right: true, } break case 7: mousestate = { left: true, center: true, right: true, } break } } canvas().oncontextmenu = (e) => e.preventDefault() canvas().onmousedown = mousechanged canvas().onmouseup = mousechanged canvas().addEventListener("click", async () => { if (pointerlock_active) await canvas().requestPointerLock({ unadjustedMovement: true, }) mousepos.x = 0 mousepos.y = 0 }) var messagelisteners = [] var pointerlock_active = false var mousepos = { x: 0, y: 0, } canvas().onmousemove = (e) => { if (pointerlock_active && !!document.pointerLockElement) { mousepos.x = e.movementX mousepos.y = -e.movementY if (Math.abs(mousepos.x) < 5) mousepos.x = 0 if (Math.abs(mousepos.y) < 5) mousepos.y = 0 scratchvar("__mousex", mousepos.x) scratchvar("__mousey", mousepos.y) } } if (!Array.prototype.none) Object.defineProperty(Array.prototype, "none", { value: function (callback) { return !this.some(callback) }, }) var rawdata window.vm = window?.vm || document .querySelector("#app") ._reactRootContainer._internalRoot.current.child.stateNode.store.getState() .scratchGui.vm const stage = vm.runtime.getTargetForStage() newfunc("vm.runtime._step", () => { if (!pointerlock_active && !document.pointerLockElement) { mousepos.x = vm.runtime.ioDevices.mouse._scratchX mousepos.y = vm.runtime.ioDevices.mouse._scratchY scratchvar("__mousex", mousepos.x) scratchvar("__mousey", mousepos.y) if (pointerlock_active) { mousepos.x = 0 mousepos.y = 0 } } }) document.onpointerlockchange = () => { scratchvar("__pointerlock", !!document.pointerLockElement) } function scratchlist(listname, value, spritename = aaa?.spritename) { if (value === undefined && /^\[\]$/.test(listname)) return JSON.parse(listname) if (value !== undefined) { if (gettarget(spritename)?.getvar(listname, "list")) gettarget(spritename).getvar(listname, "list").value = [...value] else console.warn(`list "${listname}" does not exist`) } else { return gettarget(spritename)?.getvar(listname, "list")?.value } } function scratchvar(varname, value, spritename = aaa?.spritename) { if (value !== undefined) { if (varname === "__out") { scratchvar("__loading", "done", spritename) if (scratchlist("debug:outputlog", undefined, spritename)) updatelist( "debug:outputlog", (e) => { e[e.length - 1] = value return e }, spritename, ) if (aaa?.before && aaa.before !== "__out") { if (aaa.before.includes("list:")) { updatelist( aaa.before.replace("list:", ""), (e) => { e.push(String(value)) return e }, undefined, ) } else scratchvar(aaa?.before, String(value), varname) return } } if (varname === "__error") scratchvar("__loading", "error") if (gettarget(spritename)?.getvar(varname)) gettarget(spritename).getvar(varname).value = String(value) else { if ( [ "__mousey", "__mousex", "__out", "__error", "__loading", "__script loaded", ].includes(varname) ) return console.warn(`var "${varname}" does not exist`) } } else { return gettarget(spritename)?.getvar(varname)?.value } } const projectid = location.href.match(/(?<=\/)[0-9]+(?=\/)/)?.[0] || "local" listen(window, "keydown", (e) => { var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf( e.key.toUpperCase(), ) if (index !== -1) { vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1) } vm.runtime.ioDevices.keyboard._keysPressed.push(e.key.toUpperCase()) }) listen(window, "keyup", (e) => { var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf( e.key.toUpperCase(), ) if (index !== -1) { vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1) } }) function createelem(elem, data, parent) { elem = document.createElement(elem) if (data.style) data.style.split(" ").forEach((e) => { elem.classList.add(e) }) Object.assign(elem.style, data) Object.assign(elem, data) if (typeof parent == "string") parent = qs(parent) parent?.appendChild(elem) return elem } var aaa = "asd" window.__scratchextended = { inputtype: undefined, eventdata: undefined, thread: undefined, clip(data) { return new Promise(function (done, error) { if (data) navigator.clipboard .writeText(data) .then(() => { done(data) }) .catch(() => { done(false) }) else { navigator.clipboard .readText() .then((e) => { done(e) }) .catch((e) => { error(e) }) } }) }, open(a, s, d, e) { if (e) open(a, s, d).document.write(e) else open(a, s, d) return true }, popup(text, x, y, w, h) { var win if (x) { x = totype(x, "number", true) y = totype(y, "number", true) w = totype(w, "number", true) h = totype(h, "number", true) x = (screen.width / 100) * x y = (screen.height / 100) * y w = (screen.width / 100) * w h = (screen.height / 100) * h win = open("", "", `left=${x}, top=${y} width=${w},height=${h}`) } else win = open("") if (text) win.document.write(text) return win }, eval(data) { return eval(data) }, alert(data) { alert(data) }, prompt(a, s) { return prompt(a, s) }, confirm(a) { return confirm(a) }, save(key, data) { localStorage.setItem(projectid + key, data) }, load(key) { return localStorage.getItem(projectid + key) }, clearsave(a) { if (a) localStorage.removeItem(key) else { var n = localStorage.length while (n--) { var key = localStorage.key(n) if (key.startsWith(projectid)) { localStorage.removeItem(key) } } } return true }, fetch(a, s) { s = totype(s, "json") return new Promise(function (done) { if (s) fetch(a, s).then((e) => { e.text().then((e) => { return e }) }) else fetch(a).then((e) => { e.text().then((e) => { done(e) }) }) }) }, pointerlock(a) { a = totype(a, "bool") if (a) pointerlock_active = true else { pointerlock_active = false document.exitPointerLock() } }, cursor(a) { return (canvas().style.cursor = a) }, escape() { return rawdata.replace(/(['"`\\])/g, "\\$1").replaceAll("${", "\\${") }, replace(a, s, d, regex) { regex = totype(regex, "bool") || false if (regex) return a.replace( new RegExp(s.match(/(?<=\/).*(?=\/)/)[0], s.split("/").pop()), d, ) else return a.replaceAll(s, d) }, substr(a = "", s = "", d = "") { return a.substring(s - 1, d) }, press(key) { if (key == " ") key = "space" if (/^[a-z]$/.test(key)) key = key.toUpperCase() var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf(key) if (index !== -1) { vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1) } vm.runtime.ioDevices.keyboard._keysPressed.push(key) return true }, unpress(key) { if (key == " ") key = "space" if (/^[a-z]$/.test(key)) key = key.toUpperCase() var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf(key) if (index !== -1) { vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1) } return true }, pressed(keys = "", any = "") { keys = [...keys.matchAll(/[a-z:]+/g)].map((e) => e[0]) keys = keys.map((e) => e.toLowerCase()) var nokeys = [...any.matchAll(/[a-z:]+/g)].map((e) => e[0]) nokeys = nokeys.map((e) => e.toLowerCase()) var x = vm.runtime.ioDevices.keyboard._keysPressed.map((e) => e.toLowerCase(), ) if (mousestate.left) x.push("mouse:left") if (mousestate.right) x.push("mouse:right") if (mousestate.center) x.push("mouse:center") if (any === true) return keys.some((e) => x.includes(e)) else return ( keys.every((e) => x.includes(e)) && nokeys.none((e) => x.includes(e)) ) }, getkeys(key, list) { if (key == "first") { return vm.runtime.ioDevices.keyboard._keysPressed[0] } else if (key == "last") { return vm.runtime.ioDevices.keyboard._keysPressed[ vm.runtime.ioDevices.keyboard._keysPressed.length - 1 ] } else if (key) { return vm.runtime.ioDevices.keyboard._keysPressed.includes(key) } else { if (list) scratchlist(list, vm.runtime.ioDevices.keyboard._keysPressed.sort()) else return JSON.stringify( vm.runtime.ioDevices.keyboard._keysPressed.sort(), ) } }, getmouse(button) { if (button && mousestate[button] !== undefined) return mousestate[button] else return JSON.stringify(mousepos) }, fullscreen(on) { on = totype(on, "bool") if (on) canvas().requestFullscreen() else if (on === undefined) return !!document.fullscreenElement else document.exitFullscreen() return true }, clearlogs() { if (scratchlist("debug:functionlog")) scratchlist("debug:functionlog", []) if (scratchlist("debug:datalog")) scratchlist("debug:datalog", []) if (scratchlist("debug:outputlog")) scratchlist("debug:outputlog", []) return true }, doesitexist(func) { return Object.keys(__scratchextended).includes(func) }, requestperms(...d) { if ( ( JSON.parse(localStorage.getItem("allowed:" + projectid)) || [] ).includes("all") ) return true var perms = [ ...(JSON.parse(localStorage.getItem("allowed:" + projectid)) || []), ...(JSON.parse(localStorage.getItem("denied:" + projectid)) || []), ...(JSON.parse(sessionStorage.getItem("temprevoked:" + projectid)) || []), ] data = d.filter((o) => !perms.some((i) => i === o)) if (data.length) if (confirm(`grant permissions for "${data.join('", "')}"?`)) { perms = [ ...new Set([ ...(JSON.parse(localStorage.getItem("allowed:" + projectid)) || []), ...data, ]), ] localStorage.setItem("allowed:" + projectid, JSON.stringify(perms)) return true } else { var perms = [ ...new Set([ ...(JSON.parse( sessionStorage.getItem("temprevoked:" + projectid), ) || []), ...data, ]), ] sessionStorage.setItem( "temprevoked:" + projectid, JSON.stringify(perms), ) return false } return ( d.filter((e) => JSON.parse(localStorage.getItem("allowed:" + projectid)).includes(e), ).length === d.length ) }, indexof(x, y) { return x.indexOf(y + 1) }, matches(x = "", y = "") { return y.test( new RegExp(x.match(/(?<=\/).*(?=\/)/)[0], x.split("/").pop()), ) }, lowercase(data = "") { return String(data.toLowerCase()) }, uppercase(data = "") { return String(data.toUpperCase()) }, places(num, places = 3) { num = totype(num, "number", true) places = totype(places, "number", true) return String(Number(num).toFixed(Number(places))) }, turbo(on) { on = totype(on, "bool") if (on === undefined) return vm.runtime.turboMode else vm.setTurboMode(on) }, savefile(data = "", name = "temp.txt") { const blob = new Blob([data]) const url = URL.createObjectURL(blob) const link = document.createElement("a") link.href = url link.download = name document.body.appendChild(link) link.click() link.remove() URL.revokeObjectURL(url) return true }, readfile(accept, readas = "text") { //accept:"image/png, image/jpeg" return new Promise((resolve) => { var s = createelem("input", { type: "file", accept }, document.body) s.click() s.onchange = () => { file = s.files[0] var f = new FileReader() f.onloadend = () => { resolve(f.result) } f.onerror = (e) => { console.warn(e) scratchvar("__error", e) scratchvar("__loading", "error") } if (readas == "text") f.readAsText(file) if (readas == "dataurl") f.readAsDataURL(file) if (readas == "binary") f.readAsBinaryString(file) } }) }, split(z, split, list, spritename) { //splits "z:string" by "split:string" and returns it if (list) scratchlist(list, z.split(split), spritename) else return JSON.stringify(z.split(split)) }, join(list, join) { //joins "list:name of list" by "join:string" return scratchlist(list, scratchlist(list).join(join)) }, var(varname, value, sprite) { return scratchvar(varname, value, sprite) }, list(list, data) { //if "data:set": replaces "list:name of list" with "data: an array" elsereturns "list:name of list" as as an array if (data) scratchlist( list, JSON.parse(data).map((e) => { return typeof e == "object" ? JSON.stringify(e) : e }), ) else return JSON.stringify(scratchlist(list)) }, fromms(ms = 0) { ms = totype(ms, "number", true) return JSON.stringify({ years: Math.floor(ms / 1000 / 60 / 60 / 24 / 360), days: Math.floor(ms / 1000 / 60 / 60 / 24) % 365, hours: Math.floor(ms / 1000 / 60 / 60) % 24, mins: Math.floor(ms / 1000 / 60) % 60, secs: Math.floor(ms / 1000) % 60, ms: Math.floor(ms) % 1000, }) }, toms(ms = 0, secs = 0, mins = 0, hours = 0, days = 0, years = 0) { ms = totype(ms, "number", true) secs = totype(secs, "number", true) mins = totype(mins, "number", true) hours = totype(hours, "number", true) days = totype(days, "number", true) years = totype(years, "number", true) return ( ms + secs * 1000 + mins * 60000 + hours * 3600000 + days * 86400000 + years * 31560000000 ) }, keeponlyone(arr) { arr = totype(arr, "list", true) arr = arr.filter((e, i) => i == arr.indexOf(e)) if (scratchlist(a)) scratchlist(a, arr) else return JSON.stringify(arr) }, json(json, value = "") { json = totype(json, "json", true) if (value.includes("/")) value.split("/").forEach((e) => { json = json[e] }) else json = json[value] //if(json.reverse) return typeof json == "string" || typeof json == "number" ? json : JSON.stringify(json) }, filter(arr, filter) { arr = totype(arr, "list", true) arr = arr.filter(() => eval(filter)) if (scratchlist(list)) scratchlist(list, arr) else return JSON.stringify(arr) }, map(arr, map) { arr = totype(arr, "list", true) arr = arr.map(() => eval(map)) if (scratchlist(list)) scratchlist(list, arr) else return JSON.stringify(arr) }, power(x, y) { x = totype(x, "number", true) y = totype(y, "number", true) return Math.pow(x, y) }, createvar(varname, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") gettarget(sprite).createVariable(varname, varname, "", sprite) return true }, deletevar(varname, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") if (gettarget(sprite).getvar(varname, "")?.id) { gettarget(sprite).deleteVariable( gettarget(sprite).getvar(varname, "").id, ) return true } else { return false } }, createlist(list, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") return true gettarget(sprite).createVariable(list, list, "list", sprite) }, deletelist(list, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") if (gettarget(sprite).getvar(list, "list")?.id) { gettarget(sprite).deleteVariable( gettarget(sprite).getvar(list, "list").id, ) return true } else { return false } }, getallvars() { return JSON.stringify(vm.runtime.getAllVarNamesOfType()) }, math(func, ...num) { num = num.map((e) => totype(e, "number", true)) if (num !== undefined) return Math[func].call(this, ...num) else return Math[func] }, showvar(varname, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") if (gettarget(sprite).getvar(varname, "")?.id) { vm.runtime._primitives.data_showvariable({ VARIABLE: gettarget(sprite).getvar(varname, ""), }) return true } else { return false } }, showlist(list, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") if (gettarget(sprite).getvar(list, "list")?.id) { vm.runtime._primitives.data_showlist({ LIST: gettarget(sprite).getvar(list, "list"), }) return true } else { return false } }, hidevar(varname, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") if (gettarget(sprite).getvar(varname, "")?.id) { vm.runtime._primitives.data_hidevariable({ VARIABLE: gettarget(sprite).getvar(varname, ""), }) return true } else { return false } }, hidelist(list, sprite) { if (!gettarget(sprite)) return scratchvar("__error", "sprite does not exist") if (gettarget(sprite).getvar(list, "list")?.id) { vm.runtime._primitives.data_hidelist({ LIST: gettarget(sprite).getvar(list, "list"), }) return true } else { return false } }, console(type, ...data) { console[type](...data) }, distance(x1, y1, x2, y2) { x1 = totype(x1, "number", true) y1 = totype(y1, "number", true) x2 = totype(x2, "number", false) ?? window.__scratchextended.eventdata.x y2 = totype(y2, "number", false) ?? window.__scratchextended.eventdata.y return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) }, pointto(x, y) { x = totype(x, "number") y = totype(y, "number") return gettarget( window.__scratchextended.eventdata.sprite.name, ).setDirection( scratch_math( "atan", (window.__scratchextended.eventdata.x - x) / (window.__scratchextended.eventdata.y - y), ) + (window.__scratchextended.eventdata.y >= y) * 180, ) }, onmessage(message) { if (!message) { return new Promise(function (done) { const index = messagelisteners.length messagelisteners.push(function (e) { delete messagelisteners[index] if (window.__scratchextendendebug) console.log(messagelisteners) done(e) }) }) } else if (/^\/(.*)\/[gimsuy]$/.test(message)) { return new Promise(function (done) { const index = messagelisteners.length messagelisteners.push(function (e) { if ( new RegExp( message.match(/^\/(.*)\/([gimsuy]*)$/)[1], message.match(/^\/(.*)\/([gimsuy]*)$/)[2], ).test(e) ) { delete messagelisteners[index] if (window.__scratchextendendebug) console.log(messagelisteners) done(e) } }) }) } else { return new Promise(function (done) { const index = messagelisteners.length messagelisteners.push(function (e) { if (e == message) { delete messagelisteners[index] if (window.__scratchextendendebug) console.log(messagelisteners) done(e) } }) }) } }, colorat(x, y, a) { x = totype(x, "number", true) y = totype(y, "number", true) a = totype(a, "bool") || false return new Promise(function (done) { var gl = canvas().getContext("webgl") || canvas().getContext("webgl2") var pixels = new Uint8Array(4) const xx = x + 240 const yy = y + 180 test() function test() { gl.readPixels(xx, yy, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels) if (pixels.join(",") === "0,0,0,255") setTimeout(test, 0) else { var c = [...pixels] if (!a) c.pop() done(JSON.stringify([...c])) } } }) }, arrdistance(arr1, arr2) { arr1 = totype(arr1, "list", true) arr2 = totype(arr2, "list", true) arr1 = [...arr1.matchAll(/[\-0-9]+/g)].map((e) => Number(e[0])) arr2 = [...arr2.matchAll(/[\-0-9]+/g)].map((e) => Number(e[0])) return dist(arr1, arr2) function dist(x, y) { var d = 0 x.forEach((_e, i) => { d += x[i] - y[i] }) return Math.abs(d) } }, clickat(x, y, time) { x = totype(x, "number", true) y = totype(y, "number", true) time = totype(time, "number") return new Promise(function (done) { x = Number(x) y = -Number(y) x += qol.rect(canvas()).x + 240 y += qol.rect(canvas()).y + 180 canvas().dispatchEvent( new MouseEvent("click", { view: window, bubbles: true, cancelable: true, screenX: x, screenY: y, clientX: x, clientY: y, }), ) canvas().dispatchEvent( new MouseEvent("mousedown", { view: window, bubbles: true, cancelable: true, screenX: x, screenY: y, clientX: x, clientY: y, }), ) setTimeout(() => { canvas().dispatchEvent( new MouseEvent("mouseup", { view: window, bubbles: true, cancelable: true, screenX: x, screenY: y, clientX: x, clientY: y, }), ) done("") }, time || 1) }) }, nextanswer(answer) { return new Promise(function (done) { var last = vm.runtime._events.QUESTION vm.runtime._events.QUESTION = () => { vm.runtime._events.ANSWER(answer) setTimeout(() => { vm.runtime._events.ANSWER(answer) vm.runtime._events.QUESTION = last done(answer || "") }) } }) }, jsonkeys(json, list, sprite) { if (scratchlist(list, undefined, sprite)) return scratchlist(list, Object.keys(JSON.parse(json)), sprite) else return JSON.stringify(Object.keys(JSON.parse(json))) }, jsonvalues(json, list, sprite) { if (scratchlist(list, undefined, sprite)) return scratchlist(list, Object.values(JSON.parse(json)), sprite) else return JSON.stringify(Object.values(JSON.parse(json))) }, compress(data, value) { value = totype(value, "number") try { return _c(data, value).value } catch (e) { error(e) return "ERROR" } }, decompress(data) { try { return de_c(data) } catch (e) { error(e) return "ERROR" } }, pausetimer() { vm.runtime.ioDevices.clock.pause() }, resumetimer() { vm.runtime.ioDevices.clock.resume() }, tempvar(name, value) { return (window.__scratchextended.thread[name] = value ?? window.__scratchextended.thread[name]) }, testfunction(...a) { window.testfunction(...a) }, } scratchvar("__script loaded", "true") send("__script loaded") function run(func, args) { scratchvar("__script loaded", "true") data = args if (scratchlist("debug:functionlog")) updatelist( "debug:functionlog", (e) => { e.push(func) return e }, undefined, ) if (scratchlist("debug:datalog")) updatelist( "debug:datalog", (e) => { e.push(JSON.stringify(args)) return e }, undefined, ) if (scratchlist("debug:outputlog")) updatelist( "debug:outputlog", (e) => { e.push("") return e }, undefined, ) if (window.__scratchextendendebug) console.log(func, data) try { var perms = JSON.parse(localStorage.getItem("allowed:" + projectid)) || [] var req = func.toLowerCase() var noperms = JSON.parse(localStorage.getItem("denied:" + projectid)) || [] if (noperms.includes("all") || noperms.includes(req)) return var defaultperms = JSON.parse(localStorage.getItem("scratch:defaultperms")) || [] if ( ["requestperms", "doesitexist"].includes(req) || perms.includes(req) || defaultperms.includes(req) || perms.includes("all") || defaultperms.includes("all") ) try { return window.__scratchextended[req](...args) } catch (E) { scratchvar("__loading", "error") console.trace(E) } else { if (noperms.includes(req)) { localStorage.setItem("denied:" + projectid, JSON.stringify(noperms)) return } var c = confirm(`grant permission for "${req}"?`) if (c) { perms.push(req) localStorage.setItem("allowed:" + projectid, JSON.stringify(perms)) return window.__scratchextended[req](...args) } else { noperms.push(req) localStorage.setItem("denied:" + projectid, JSON.stringify(noperms)) } } } catch (e) { scratchvar("__loading", "error") console.trace(e) } } // vm.runtime._events.SAY = (event, type, rawdata) => { // datatype = "say" // window.__scratchextended.eventdata = event // if (__scratchextended[rawdata?.match(/^[a-z0-9]*?(?=[ '"`(])/i)?.[0]]) // run(rawdata?.match(/^[a-z0-9]*?(?=[ '"`(])/i)?.[0], parse(rawdata)) // } function updatelist(listname, func, spritename) { scratchlist( listname, func([...scratchlist(listname, undefined, spritename)]), spritename, ) } function parse(data, canhavevar) { rawdata = data var rand = Math.random() data = data.replaceAll(/\\\\/g, rand).replaceAll("\\n", "\n") aaa = ( data.match( /(?:(?<!\\)(["'`])[^]*?(?<!\\)\1)|(?<=\()(?<!(["'`]))[^]*(?=\))/gim, ) || [] ).map((e) => { e = e .replace(/\\(['"`])/g, "$1") .replace(/^["'`]/, "") .replace(/["'`]$/, "") .replaceAll(rand, "\\") if (canhavevar) e = e .replaceAll( /(?<!\\)\$\{([a-z0-9_!@#$%^&*() ,/;'\[\]\\:"<>?\{|~`+\}]+?(?:\.[a-z0-9_!@#$%^&*() ,/;'\[\]\\:"<>?\{|~`+\}]+?)?)\}/g, (_full, one) => window.__scratchextended.thread[one] ?? (one.includes(".") ? scratchvar(one.split(".")[1], undefined, one.split(".")[0]) : scratchvar(one)), ) .replaceAll("\\${", "${") return e }) return aaa } function newfunc(func1, func2) { eval(`var a = ${func1} var s = ${func2} ${func1} = function (...args) { try{ s(arguments) } catch(e){console.error("${func1.replaceAll('"', '\\"')}", e)} return a.call(this, ...args) }`) } function gettarget(sprite) { if (sprite) var x = vm.runtime.getSpriteTargetByName(sprite) else var x = vm.runtime.getTargetForStage() x.getvar = x.lookupVariableByNameAndType return x } window.gettarget = gettarget window.stage = stage function totype(inp, type, forced) { //number, string, list, object, json, bool try { switch (type) { case "number": if (inp == "true") inp = 1 if (inp == "false") inp = 0 if (/^-?[0-9]*\.?[0-9]+$/.test(inp)) return Number(inp) if (inp === "NaN" || inp == "nan") return NaN return fail(inp, type, forced) case "string": return inp case "list": inp = JSON.parse(inp) if (inp.reverse) return inp return fail(inp, type, forced) case "object": inp = JSON.parse(inp.replaceAll("'", '"').replaceAll("`", '"')) if (/^[\-0-9]+$/.test(inp) || inp === true || inp === false) return undefined if (Object.keys(inp).length !== undefined && inp.length === undefined) return inp return fail(inp, type, forced) case "bool": if (inp === "1" || inp === "true") return true if (inp === "0" || inp === "false") return false return fail(inp, type) case "json": if ( totype(inp, "object") !== undefined || totype(inp, "list") !== undefined ) return totype(inp, "object") || totype(inp, "list") else { fail(inp, type, forced) } } } catch (s) { return fail(inp, type, forced) } function fail(inp, type, forced) { if (forced) { throw new Error(`"${inp}" must be of type "${type}"`) } else return undefined } } function scratch_math(operator, n) { switch (operator) { case "sin": return Math.round(Math.sin((Math.PI * n) / 180) * 1e10) / 1e10 case "cos": return Math.round(Math.cos((Math.PI * n) / 180) * 1e10) / 1e10 case "asin": return (Math.asin(n) * 180) / Math.PI case "acos": return (Math.acos(n) * 180) / Math.PI case "atan": return (Math.atan(n) * 180) / Math.PI case "log": return Math.log(n) / Math.LN10 } return 0 } function send(data) { vm.runtime.startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: data, }) } newfunc("vm.runtime.startHats", function (...args) { args = args[0] if (args[0] == "event_whenbroadcastreceived") { if ( !messagelisteners[messagelisteners.length - 1] && messagelisteners.length ) messagelisteners.pop() messagelisteners.forEach((e) => { e(args[1].BROADCAST_OPTION) }) if (window.__scratchextendendebug) console.log(messagelisteners) } }) var allowedchars = "asdfghjklqwertyuiopzxcvbnm,./;'[]`1234567890-=}{\":?><|+_)(*&^%$#@!~\\ASDFGHJKLZXCVBNMQWERTYUIOP " function best_c(data) { var arr = [Infinity, ""] for (var i = 1; i++ < 20; ) { var s = _c(data, i).value if (s.length < arr[0]) arr = [s.length, s, i] } return { value: arr[1], p: Math.round((s.length / data.length) * 10000) / 100, } } var asd = [] for (var i = 100; i++ < 999; ) { var c = String(i) while (c.length < 4) c = "1" + c eval(`c = "\\u${c}"`) if (c) asd.push(c) } asd = asd.filter((e) => { return asd.join("").includes(e) }) asd = asd.filter((e) => !allowedchars.split("").includes(e)) function de_c(text) { const split1 = "\u1000" const split2 = "\u1001" text = text.split(split1) var arr = text[0].split(split2) arr.forEach((e, i) => { text[1] = text[1].replaceAll(asd[i], e) }) return text[1] } function _c(t, jj) { if (!jj) return best_c(t) var origlength = t.length const split1 = "\u1000" function g(count) { var d = "" while (count-- > 0) { d += "." } return d } var dots = g(jj) const split2 = "\u1001" var t1 = [...t.matchAll(new RegExp(dots, "g"))] var c = t.split("") c.shift() c = c.join("") var t2 = [...c.matchAll(new RegExp(dots, "g"))] t1 = t1.map((e) => JSON.stringify(e)) t2 = t2.map((e) => JSON.stringify(e)) var t1dupes = t1.length - keeponlyone([...t1]).length var t2dupes = t2.length - keeponlyone([...t2]).length var output = t2dupes > t1dupes ? t2 : t1 var arr = [] output.forEach((e, i) => { if (output.indexOf(e) !== i) arr.push(JSON.parse(e)) }) arr = keeponlyone(arr.flat()) var x = arr.join(split2) arr.forEach((e, i) => { t = t.replaceAll(e, asd[i]) }) return { value: x + split1 + t, p: Math.round((t.length / origlength) * 10000) / 100, } } } function canvas() { return ( window?.vm?.runtime?.renderer?.canvas || document.querySelector( "#app > div > div.gui_body-wrapper_-N0sA.box_box_2jjDp > div > div.gui_stage-and-target-wrapper_69KBf.box_box_2jjDp > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas", ) || document.querySelector( "#view > div > div.inner > div:nth-child(2) > div.guiPlayer > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas", ) || document.querySelector( "#app > div > div > div > div.gui_body-wrapper_-N0sA.box_box_2jjDp > div > div.gui_stage-and-target-wrapper_69KBf.box_box_2jjDp > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas", ) ) } listen(window, "mousemove", function () { if (!location.href.includes("/editor")) return if (!window?.vm?.editingTarget?.comments) return var temp = false a(vm.editingTarget.comments).foreach(function (a, s) { var f = vm.editingTarget.blocks._blocks[s.blockId] if (!f) return if (f.comment !== a) { f.comment = a return } if (f?.opcode !== "operator_gt") return if ( vm.editingTarget.blocks._blocks[f.inputs.OPERAND2.block]?.fields?.TEXT ?.value == a ) return if ( vm.editingTarget.blocks._blocks[f.inputs.OPERAND2.block]?.fields?.TEXT ?.value === undefined ) return vm.editingTarget.blocks._blocks[f.inputs.OPERAND2.block].fields.TEXT.value = a f.comment = a return (temp = true) }) if (temp) vm.emitWorkspaceUpdate() a(vm.editingTarget.comments).foreach(function (a, s) { var f = vm.editingTarget.blocks._blocks[s.blockId] if (!f) delete vm.editingTarget.comments[a] else f.comment = a }) }) function listen(elem, type, cb) { elem.addEventListener(type, cb) return [elem, type, cb] } function unlisten(data) { return data[0].removeEventListener(data[1], data[2]) } function foreachobj(arr, func) { Reflect.ownKeys(arr).forEach((e, i) => { func(e, arr[e], i) }) } function keeponlyone(arr) { return [...new Set(arr)] }