From dbbd2c2cb8ce39f5ee2f9bef32ae8167c07d564b Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:20:42 -0400 Subject: [PATCH 01/14] Fix errors in Pull --- extensions/obviousAlexC/penPlus.js | 2651 ++++++++++++++++++++++++++++ 1 file changed, 2651 insertions(+) create mode 100644 extensions/obviousAlexC/penPlus.js diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js new file mode 100644 index 0000000000..c3866f2695 --- /dev/null +++ b/extensions/obviousAlexC/penPlus.js @@ -0,0 +1,2651 @@ +(function (Scratch) { + "use strict"; + + //?some smaller optimizations just store the multiplacation for later + const f32_4 = 4 * Float32Array.BYTES_PER_ELEMENT; + const f32_8 = 8 * Float32Array.BYTES_PER_ELEMENT; + const f32_10 = 10 * Float32Array.BYTES_PER_ELEMENT; + const d2r = 0.0174533; + + //?Declare most of the main repo's we are going to use around the scratch vm + const vm = Scratch.vm; + const runtime = vm.runtime; + const renderer = runtime.renderer; + const shaderManager = renderer._shaderManager; + + const canvas = renderer.canvas; + const gl = renderer._gl; + let currentFilter = gl.NEAREST; + + let nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; + + //?create the depth buffer's texture + //*Create it in scratch's gl so that we have it stored in there! + let depthBufferTexture = gl.createTexture(); + + //?Make a function for updating the depth canvas to fit the scratch stage + const depthFrameBuffer = gl.createFramebuffer(); + const depthColorBuffer = gl.createRenderbuffer(); + const depthDepthBuffer = gl.createRenderbuffer(); + + let lastFB = gl.getParameter(gl.FRAMEBUFFER_BINDING); + + //?Buffer handling and pen loading + { + gl.bindTexture(gl.TEXTURE_2D, depthBufferTexture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + nativeSize[0], + nativeSize[1], + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + null + ); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, depthBufferTexture); + gl.activeTexture(gl.TEXTURE0); + + gl.bindFramebuffer(gl.FRAMEBUFFER, depthFrameBuffer); + + gl.bindRenderbuffer(gl.RENDERBUFFER, depthColorBuffer); + gl.renderbufferStorage( + gl.RENDERBUFFER, + gl.RGBA8 || gl.RGBA4, + nativeSize[0], + nativeSize[1] + ); + gl.framebufferRenderbuffer( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.RENDERBUFFER, + depthColorBuffer + ); + + gl.bindRenderbuffer(gl.RENDERBUFFER, depthDepthBuffer); + gl.renderbufferStorage( + gl.RENDERBUFFER, + gl.DEPTH_COMPONENT16, + nativeSize[0], + nativeSize[1] + ); + gl.framebufferRenderbuffer( + gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, + depthDepthBuffer + ); + + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + depthBufferTexture, + 0 + ); + + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + + gl.bindFramebuffer(gl.FRAMEBUFFER, lastFB); + + const updateCanvasSize = () => { + nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; + + lastFB = gl.getParameter(gl.FRAMEBUFFER_BINDING); + + gl.bindFramebuffer(gl.FRAMEBUFFER, depthFrameBuffer); + + gl.bindRenderbuffer(gl.RENDERBUFFER, depthColorBuffer); + gl.renderbufferStorage( + gl.RENDERBUFFER, + gl.RGBA8 || gl.RGBA4, + nativeSize[0], + nativeSize[1] + ); + + gl.bindRenderbuffer(gl.RENDERBUFFER, depthDepthBuffer); + gl.renderbufferStorage( + gl.RENDERBUFFER, + gl.DEPTH_COMPONENT16, + nativeSize[0], + nativeSize[1] + ); + + gl.activeTexture(gl.TEXTURE1); + + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + nativeSize[0], + nativeSize[1], + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + null + ); + + gl.activeTexture(gl.TEXTURE0); + + gl.bindFramebuffer(gl.FRAMEBUFFER, lastFB); + }; + + //?Call it to have it consistant + updateCanvasSize(); + + //?Call every frame because I don't know of a way to detect when the stage is resized + + window.addEventListener("resize", updateCanvasSize); + vm.runtime.on("STAGE_SIZE_CHANGED", () => { + updateCanvasSize(); + }); + + let lastURL = window.location.href; + let lastHQPen = renderer.useHighQualityRender; + + vm.runtime.on("BEFORE_EXECUTE", () => { + if ( + lastURL != window.location.href || + lastHQPen != renderer.useHighQualityRender + ) { + lastURL = window.location.href; + lastHQPen = renderer.useHighQualityRender; + updateCanvasSize(); + } + }); + + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + + //?Make sure pen is loaded! + if (!Scratch.vm.extensionManager.isExtensionLoaded("pen")) { + runtime.extensionManager.loadExtensionIdSync("pen"); + } + } + + //?Ported from Pen+ version 5 + //?Just a costume library for data uris + const penPlusCostumeLibrary = {}; + let penPlusImportWrapMode = gl.CLAMP_TO_EDGE; + + //?Debug for depth + penPlusCostumeLibrary["!Debug_Depth"] = depthBufferTexture; + + const checkForPen = (util) => { + const curTarget = util.target; + const customState = curTarget["_customState"]; + if (!customState["Scratch.pen"]) { + customState["Scratch.pen"] = { + penDown: false, + color: 66.66, + saturation: 100, + brightness: 100, + transparency: 0, + _shade: 50, + penAttributes: { + color4f: [0, 0, 1, 1], + diameter: 1, + }, + }; + } + }; + + //*Define PEN+ variables >:) + const triangleDefaultAttributes = [ + // U V TINT R G B Z W transparency U V TINT R G B Z W transparency U V TINT R G B Z W transparency + 0, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 1, + ]; + const squareDefaultAttributes = [ + // width* height* rotation u-mul u v-mul v r g b transparency + 1, 1, 90, 1, 0, 1, 0, 1, 1, 1, 1, 1, + ]; + + const triangleAttributesOfAllSprites = {}; //!it dawned on me I have to do this + + const squareAttributesOfAllSprites = {}; //?Doing this for part 2 + + //?Get Shaders + const penPlusShaders = { + untextured: { + Shaders: { + vert: ` + attribute highp vec4 a_position; + attribute highp vec4 a_color; + varying vec4 v_color; + + varying highp float v_depth; + + void main() + { + v_color = a_color; + v_depth = a_position.z; + gl_Position = a_position * vec4(a_position.w,a_position.w,0,1); + } + `, + frag: ` + varying highp vec4 v_color; + + uniform mediump vec2 u_res; + uniform sampler2D u_depthTexture; + + varying highp float v_depth; + + void main() + { + gl_FragColor = v_color; + highp vec4 v_depthPart = texture2D(u_depthTexture,gl_FragCoord.xy/u_res); + highp float v_depthcalc = (v_depthPart.r+v_depthPart.g+v_depthPart.b)/3.0; + if (v_depthcalc < v_depth){ + gl_FragColor.a = 0.0; + } + gl_FragColor.rgb *= gl_FragColor.a; + } + `, + }, + ProgramInf: null, + }, + textured: { + Shaders: { + vert: ` + attribute highp vec4 a_position; + attribute highp vec4 a_color; + attribute highp vec2 a_texCoord; + + varying highp vec4 v_color; + varying highp vec2 v_texCoord; + + varying highp float v_depth; + + void main() + { + v_color = a_color; + v_texCoord = a_texCoord; + v_depth = a_position.z * 3.0; + gl_Position = a_position * vec4(a_position.w,a_position.w,0,1); + } + `, + frag: ` + uniform sampler2D u_texture; + + varying highp vec2 v_texCoord; + varying highp vec4 v_color; + + uniform mediump vec2 u_res; + uniform sampler2D u_depthTexture; + + varying highp float v_depth; + + void main() + { + gl_FragColor = texture2D(u_texture, v_texCoord) * v_color; + highp vec4 v_depthPart = texture2D(u_depthTexture,gl_FragCoord.xy/u_res); + highp float v_depthcalc = (v_depthPart.r+v_depthPart.g+v_depthPart.b); + if (v_depthcalc < v_depth){ + gl_FragColor.a = 0.0; + } + gl_FragColor.rgb *= gl_FragColor.a; + + } + `, + }, + ProgramInf: null, + }, + depth: { + Shaders: { + vert: ` + attribute highp vec4 a_position; + + varying highp float v_depth; + + void main() + { + v_depth = a_position.z*3.0; + gl_Position = a_position * vec4(a_position.w,a_position.w,a_position.w,1); + } + `, + frag: ` + varying highp float v_depth; + + void main() + { + gl_FragColor = vec4(v_depth,0,0,1); + if (v_depth>1.0) { + gl_FragColor.r = 1.0; + gl_FragColor.g = v_depth-1.0; + if (v_depth>2.0) { + gl_FragColor.g = 1.0; + gl_FragColor.b = v_depth-2.0; + } + } + } + `, + }, + ProgramInf: null, + }, + pen: { + program: null, + }, + createAndCompileShaders: (vert, frag) => { + //? compile vertex Shader + const vertShader = gl.createShader(gl.VERTEX_SHADER); + try { + gl.shaderSource(vertShader, vert.trim()); + gl.compileShader(vertShader); + if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(vertShader); + } + } catch (error) { + console.error(error); + } + + //? compile fragment Shader + const fragShader = gl.createShader(gl.FRAGMENT_SHADER); + try { + gl.shaderSource(fragShader, frag.trim()); + gl.compileShader(fragShader); + if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { + throw gl.getShaderInfoLog(fragShader); + } + } catch (error) { + console.error(error); + } + + //? compile program + const program = gl.createProgram(); + try { + gl.attachShader(program, vertShader); + gl.attachShader(program, fragShader); + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + throw gl.getProgramInfoLog(program); + } + + gl.validateProgram(program); + if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) { + throw gl.getProgramInfoLog(program); + } + } catch (error) { + console.error(error); + } + + return { + program: program, + vert: vertShader, + frag: fragShader, + }; + }, + }; + + //? Create program info + { + penPlusShaders.untextured.ProgramInf = + penPlusShaders.createAndCompileShaders( + penPlusShaders.untextured.Shaders.vert, + penPlusShaders.untextured.Shaders.frag + ); + penPlusShaders.textured.ProgramInf = penPlusShaders.createAndCompileShaders( + penPlusShaders.textured.Shaders.vert, + penPlusShaders.textured.Shaders.frag + ); + + penPlusShaders.depth.ProgramInf = penPlusShaders.createAndCompileShaders( + penPlusShaders.depth.Shaders.vert, + penPlusShaders.depth.Shaders.frag + ); + } + + //?Untextured + const a_position_Location_untext = gl.getAttribLocation( + penPlusShaders.untextured.ProgramInf.program, + "a_position" + ); + const a_color_Location_untext = gl.getAttribLocation( + penPlusShaders.untextured.ProgramInf.program, + "a_color" + ); + + //?Textured + const a_position_Location_text = gl.getAttribLocation( + penPlusShaders.textured.ProgramInf.program, + "a_position" + ); + const a_color_Location_text = gl.getAttribLocation( + penPlusShaders.textured.ProgramInf.program, + "a_color" + ); + const a_textCoord_Location_text = gl.getAttribLocation( + penPlusShaders.textured.ProgramInf.program, + "a_texCoord" + ); + + //?Uniforms + const u_texture_Location_text = gl.getUniformLocation( + penPlusShaders.textured.ProgramInf.program, + "u_texture" + ); + + const u_depthTexture_Location_untext = gl.getUniformLocation( + penPlusShaders.untextured.ProgramInf.program, + "u_depthTexture" + ); + + const u_depthTexture_Location_text = gl.getUniformLocation( + penPlusShaders.textured.ProgramInf.program, + "u_depthTexture" + ); + + const u_res_Location_untext = gl.getUniformLocation( + penPlusShaders.untextured.ProgramInf.program, + "u_res" + ); + + const u_res_Location_text = gl.getUniformLocation( + penPlusShaders.textured.ProgramInf.program, + "u_res" + ); + + //?Depth + const a_position_Location_depth = gl.getAttribLocation( + penPlusShaders.depth.ProgramInf.program, + "a_position" + ); + + //?Enables Attributes + const vertexBuffer = gl.createBuffer(); + const depthVertexBuffer = gl.createBuffer(); + let vertexBufferData = null; + + { + gl.enableVertexAttribArray(a_position_Location_untext); + gl.enableVertexAttribArray(a_color_Location_untext); + gl.enableVertexAttribArray(a_position_Location_text); + gl.enableVertexAttribArray(a_color_Location_text); + gl.enableVertexAttribArray(a_textCoord_Location_text); + gl.enableVertexAttribArray(a_position_Location_depth); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl.ARRAY_BUFFER, depthVertexBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + } + + //?Override pen Clear with pen+ + renderer.penClear = (penSkinID) => { + const lastCC = gl.getParameter(gl.COLOR_CLEAR_VALUE); + lastFB = gl.getParameter(gl.FRAMEBUFFER_BINDING); + //Pen+ Overrides default pen Clearing + gl.bindFramebuffer(gl.FRAMEBUFFER, depthFrameBuffer); + gl.clearColor(1, 1, 1, 1); + gl.clear(gl.DEPTH_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.bindFramebuffer(gl.FRAMEBUFFER, lastFB); + gl.clearColor(lastCC[0], lastCC[1], lastCC[2], lastCC[3]); + + //? ^ just clear the depth buffer for when its added. + + //Old clearing + renderer.dirty = true; + const skin = /** @type {PenSkin} */ renderer._allSkins[penSkinID]; + skin.clear(); + }; + + //Pen+ advanced options update + //I plan to add more later + const penPlusAdvancedSettings = { + wValueUnderFlow: false, + _maxDepth: 1000, + }; + + //?Have this here for ez pz tri drawing on the canvas + const triFunctions = { + drawTri: (curProgram, x1, y1, x2, y2, x3, y3, penColor, targetID) => { + //? get triangle attributes for current sprite. + const triAttribs = triangleAttributesOfAllSprites[targetID]; + + if (triAttribs) { + vertexBufferData = new Float32Array([ + x1, + -y1, + triAttribs[5], + triAttribs[6], + penColor[0] * triAttribs[2], + penColor[1] * triAttribs[3], + penColor[2] * triAttribs[4], + penColor[3] * triAttribs[7], + + x2, + -y2, + triAttribs[13], + triAttribs[14], + penColor[0] * triAttribs[10], + penColor[1] * triAttribs[11], + penColor[2] * triAttribs[12], + penColor[3] * triAttribs[15], + + x3, + -y3, + triAttribs[21], + triAttribs[22], + penColor[0] * triAttribs[18], + penColor[1] * triAttribs[19], + penColor[2] * triAttribs[20], + penColor[3] * triAttribs[23], + ]); + } else { + vertexBufferData = new Float32Array([ + x1, + -y1, + 1, + 1, + penColor[0], + penColor[1], + penColor[2], + penColor[3], + + x2, + -y2, + 1, + 1, + penColor[0], + penColor[1], + penColor[2], + penColor[3], + + x3, + -y3, + 1, + 1, + penColor[0], + penColor[1], + penColor[2], + penColor[3], + ]); + } + + //? Bind Positional Data + + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vertexBufferData, gl.STATIC_DRAW); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + gl.vertexAttribPointer( + a_position_Location_untext, + 4, + gl.FLOAT, + false, + f32_8, + 0 + ); + gl.vertexAttribPointer( + a_color_Location_untext, + 4, + gl.FLOAT, + false, + f32_8, + f32_4 + ); + + gl.useProgram(penPlusShaders.untextured.ProgramInf.program); + + gl.uniform1i(u_depthTexture_Location_untext, 1); + + gl.uniform2fv(u_res_Location_untext, nativeSize); + + gl.drawArrays(gl.TRIANGLES, 0, 3); + //? Hacky fix but it works. + + triFunctions.drawDepthTri(targetID, x1, y1, x2, y2, x3, y3); + gl.useProgram(penPlusShaders.pen.program); + }, + + drawTextTri: (curProgram, x1, y1, x2, y2, x3, y3, targetID, texture) => { + //? get triangle attributes for current sprite. + const triAttribs = triangleAttributesOfAllSprites[targetID]; + + if (triAttribs) { + vertexBufferData = new Float32Array([ + x1, + -y1, + triAttribs[5], + triAttribs[6], + triAttribs[2], + triAttribs[3], + triAttribs[4], + triAttribs[7], + triAttribs[0], + triAttribs[1], + + x2, + -y2, + triAttribs[13], + triAttribs[14], + triAttribs[10], + triAttribs[11], + triAttribs[12], + triAttribs[15], + triAttribs[8], + triAttribs[9], + + x3, + -y3, + triAttribs[21], + triAttribs[22], + triAttribs[18], + triAttribs[19], + triAttribs[20], + triAttribs[23], + triAttribs[16], + triAttribs[17], + ]); + } else { + vertexBufferData = new Float32Array([ + x1, + -y1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + + x2, + -y2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + + x3, + -y3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + ]); + } + //? Bind Positional Data + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vertexBufferData, gl.DYNAMIC_DRAW); + + gl.vertexAttribPointer( + a_position_Location_text, + 4, + gl.FLOAT, + false, + f32_10, + 0 + ); + gl.vertexAttribPointer( + a_color_Location_text, + 4, + gl.FLOAT, + false, + f32_10, + f32_4 + ); + gl.vertexAttribPointer( + a_textCoord_Location_text, + 2, + gl.FLOAT, + false, + f32_10, + f32_8 + ); + + gl.useProgram(penPlusShaders.textured.ProgramInf.program); + + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, currentFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, currentFilter); + gl.uniform1i(u_texture_Location_text, 0); + + gl.uniform1i(u_depthTexture_Location_text, 1); + + gl.uniform2fv(u_res_Location_text, nativeSize); + + gl.drawArrays(gl.TRIANGLES, 0, 3); + //? Hacky fix but it works. + triFunctions.drawDepthTri(targetID, x1, y1, x2, y2, x3, y3); + gl.useProgram(penPlusShaders.pen.program); + }, + + //? this is so I don't have to go through the hassle of replacing default scratch shaders + //? many of curse words where exchanged between me and a pillow while writing this extension + //? but I have previaled! + drawDepthTri: (targetID, x1, y1, x2, y2, x3, y3) => { + lastFB = gl.getParameter(gl.FRAMEBUFFER_BINDING); + const triAttribs = triangleAttributesOfAllSprites[targetID]; + gl.bindFramebuffer(gl.FRAMEBUFFER, depthFrameBuffer); + + if (triAttribs) { + vertexBufferData = new Float32Array([ + x1, + -y1, + triAttribs[5], + triAttribs[6], + + x2, + -y2, + triAttribs[13], + triAttribs[14], + + x3, + -y3, + triAttribs[21], + triAttribs[22], + ]); + } else { + vertexBufferData = new Float32Array([ + x1, + -y1, + 0, + 1, + + x2, + -y2, + 0, + 1, + + x3, + -y3, + 0, + 1, + ]); + } + + //? Bind Positional Data + gl.bindBuffer(gl.ARRAY_BUFFER, depthVertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vertexBufferData, gl.DYNAMIC_DRAW); + + gl.vertexAttribPointer( + a_position_Location_depth, + 4, + gl.FLOAT, + false, + f32_4, + 0 + ); + + gl.useProgram(penPlusShaders.depth.ProgramInf.program); + + gl.drawArrays(gl.TRIANGLES, 0, 3); + + gl.bindFramebuffer(gl.FRAMEBUFFER, lastFB); + }, + + setValueAccordingToCaseTriangle: ( + targetId, + attribute, + value, + wholeTri, + offset + ) => { + offset = offset + attribute || attribute; + let valuetoSet = 0; + switch (attribute) { + //U + case 0: + valuetoSet = value; + break; + //V + case 1: + valuetoSet = value; + break; + + //100 since that is what scratch users are accustomed to. + //R + case 2: + valuetoSet = Math.min(Math.max(value, 0), 100) * 0.01; + break; + //G + case 3: + valuetoSet = Math.min(Math.max(value, 0), 100) * 0.01; + break; + //B + case 4: + valuetoSet = Math.min(Math.max(value, 0), 100) * 0.01; + break; + + //Clamp to 0 so we can't go behind the stage. + //Z + case 5: + if (value < 0) { + valuetoSet = 0; + break; + } + valuetoSet = Math.min(value / penPlusAdvancedSettings._maxDepth, 1); + break; + + //Clamp to 1 so we don't accidentally clip. + //W + case 6: + if (penPlusAdvancedSettings.wValueUnderFlow == true) { + valuetoSet = value; + } else { + valuetoSet = Math.max(value, 1); + } + break; + //Transparency + //Same story as color + case 7: + valuetoSet = Math.min(Math.max(value, 0), 1000) * 0.01; + break; + + //Just break if value isn't valid + default: + break; + } + //Check if the index even exists. + if (attribute >= 0 && attribute <= 7) { + if (wholeTri) { + triangleAttributesOfAllSprites[targetId][attribute] = valuetoSet; + triangleAttributesOfAllSprites[targetId][attribute + 8] = valuetoSet; + triangleAttributesOfAllSprites[targetId][attribute + 16] = valuetoSet; + } else { + triangleAttributesOfAllSprites[targetId][offset] = valuetoSet; + } + } + }, + }; + + const lilPenDabble = (InativeSize, curTarget, util) => { + checkForPen(util); + + const attrib = curTarget["_customState"]["Scratch.pen"].penAttributes; + + Scratch.vm.renderer.penLine( + Scratch.vm.renderer._penSkinId, + { + color4f: [1, 1, 1, 0.011], + diameter: 1, + }, + InativeSize[0] / 2, + InativeSize[1] / 2, + InativeSize[0] / 2, + InativeSize[1] / 2 + ); + }; + + //?Color Library + const colors = { + hexToRgb: (hex) => { + if (typeof hex == "string") { + const splitHex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return { + r: parseInt(splitHex[1], 16), + g: parseInt(splitHex[2], 16), + b: parseInt(splitHex[3], 16), + }; + } + hex = Scratch.Cast.toNumber(hex); + return { + r: Math.floor(hex / 65536), + g: Math.floor(hex / 256) % 256, + b: hex % 256, + }; + }, + + rgbtoSColor: ({ R, G, B }) => { + R = Math.min(Math.max(R, 0), 100) * 2.55; + G = Math.min(Math.max(G, 0), 100) * 2.55; + B = Math.min(Math.max(B, 0), 100) * 2.55; + return (Math.ceil(R) * 256 + Math.ceil(G)) * 256 + Math.ceil(B); + }, + }; + + const textureFunctions = { + createBlankPenPlusTextureInfo: async function ( + width, + height, + color, + name, + clamp + ) { + const texture = penPlusCostumeLibrary[name] + ? penPlusCostumeLibrary[name].texture + : gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + // Fill the texture with a 1x1 blue pixel. + + const pixelData = new Uint8Array(width * height * 4); + + const decodedColor = colors.hexToRgb(color); + + for (let pixelID = 0; pixelID < pixelData.length / 4; pixelID++) { + pixelData[pixelID * 4] = decodedColor.r; + pixelData[pixelID * 4 + 1] = decodedColor.g; + pixelData[pixelID * 4 + 2] = decodedColor.b; + pixelData[pixelID * 4 + 3] = 255; + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, clamp); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, clamp); + + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + width, + height, + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + pixelData + ); + + penPlusCostumeLibrary[name] = { + texture: texture, + width: width, + height: height, + }; + }, + createPenPlusTextureInfo: async function (url, name, clamp) { + const texture = penPlusCostumeLibrary[name] + ? penPlusCostumeLibrary[name].texture + : gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + // Fill the texture with a 1x1 blue pixel. + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + 1, + 1, + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + new Uint8Array([0, 0, 255, 255]) + ); + + // Let's assume all images are not a power of 2 + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, clamp); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, clamp); + return new Promise((resolve, reject) => { + Scratch.canFetch(url).then((allowed) => { + if (!allowed) { + reject(false); + } + // Permission is checked earlier. + // eslint-disable-next-line no-restricted-syntax + const image = new Image(); + image.onload = function () { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + gl.RGBA, + gl.UNSIGNED_BYTE, + image + ); + penPlusCostumeLibrary[name] = { + texture: texture, + width: image.width, + height: image.height, + }; + resolve(texture); + }; + image.crossOrigin = "anonymous"; + image.src = url; + }); + }); + }, + + getTextureData: (texture, width, height) => { + //?Initilize the temp framebuffer and assign it + const readBuffer = gl.createFramebuffer(); + + lastFB = gl.getParameter(gl.FRAMEBUFFER_BINDING); + + gl.bindFramebuffer(gl.FRAMEBUFFER, readBuffer); + + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + texture, + 0 + ); + + //?make sure to unbind the framebuffer and delete it! + const removeBuffer = () => { + gl.deleteFramebuffer(readBuffer); + }; + + //?if sucessful read + if ( + gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE + ) { + //?Make an array to write the pixels onto + let dataArray = new Uint8Array(width * height * 4); + gl.readPixels( + 0, + 0, + width, + height, + gl.RGBA, + gl.UNSIGNED_BYTE, + dataArray + ); + + //?Remove Buffer data and return data + removeBuffer(); + return dataArray; + } + + //?If not return undefined + removeBuffer(); + return undefined; + }, + + getTextureAsURI: (texture, width, height) => { + //?Initilize the temp framebuffer and assign it + const readBuffer = gl.createFramebuffer(); + + lastFB = gl.getParameter(gl.FRAMEBUFFER_BINDING); + + gl.bindFramebuffer(gl.FRAMEBUFFER, readBuffer); + + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + texture, + 0 + ); + + //?make sure to unbind the framebuffer and delete it! + const removeBuffer = () => { + gl.deleteFramebuffer(readBuffer); + }; + + //?if sucessful read + if ( + gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE + ) { + //?Make an array to write the pixels onto + let dataArray = new Uint8Array(width * height * 4); + gl.readPixels( + 0, + 0, + width, + height, + gl.RGBA, + gl.UNSIGNED_BYTE, + dataArray + ); + + //Make an invisible canvas + const dataURICanvas = document.createElement("canvas"); + dataURICanvas.width = width; + dataURICanvas.height = height; + const dataURIContext = dataURICanvas.getContext("2d"); + + // Copy the pixels to a 2D canvas + const imageData = dataURIContext.createImageData(width, height); + imageData.data.set(dataArray); + dataURIContext.putImageData(imageData, 0, 0); + + //?Remove Buffer data and return data + removeBuffer(); + return dataURICanvas.toDataURL(); + } + + //?If not return undefined + removeBuffer(); + return undefined; + }, + }; + + class extension { + getInfo() { + return { + blocks: [ + { + opcode: "__NOUSEOPCODE", + blockType: Scratch.BlockType.LABEL, + text: "Pen Properties", + }, + { + disableMonitor: true, + opcode: "isPenDown", + blockType: Scratch.BlockType.BOOLEAN, + text: "pen is down?", + arguments: {}, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "getPenHSV", + blockType: Scratch.BlockType.REPORTER, + text: "pen [HSV]", + arguments: { + HSV: { + type: Scratch.ArgumentType.STRING, + defaultValue: "color", + menu: "hsvMenu", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "drawDot", + blockType: Scratch.BlockType.COMMAND, + text: "draw dot at [x] [y]", + arguments: { + x: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "drawLine", + blockType: Scratch.BlockType.COMMAND, + text: "draw line from [x1] [y1] to [x2] [y2]", + arguments: { + x1: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + y1: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + x2: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + y2: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + }, + filter: "sprite", + }, + { + opcode: "__NOUSEOPCODE", + blockType: Scratch.BlockType.LABEL, + text: "Square Pen Blocks", + }, + { + disableMonitor: true, + opcode: "squareDown", + blockType: Scratch.BlockType.COMMAND, + text: "stamp pen square", + arguments: {}, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "squareTexDown", + blockType: Scratch.BlockType.COMMAND, + text: "stamp pen square with the texture of [tex]", + arguments: { + tex: { type: Scratch.ArgumentType.STRING, menu: "costumeMenu" }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "setStampAttribute", + blockType: Scratch.BlockType.COMMAND, + text: "set pen square's [target] to [number]", + arguments: { + target: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 0, + menu: "stampSquare", + }, + number: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "getStampAttribute", + blockType: Scratch.BlockType.REPORTER, + text: "Get pen square's [target]", + arguments: { + target: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 0, + menu: "stampSquare", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "tintSquare", + blockType: Scratch.BlockType.COMMAND, + text: "tint pen square to [color]", + arguments: { + color: { + type: Scratch.ArgumentType.COLOR, + defaultValue: "#0000ff", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "resetSquareAttributes", + blockType: Scratch.BlockType.COMMAND, + text: "reset square Attributes", + arguments: {}, + filter: "sprite", + }, + { + opcode: "__NOUSEOPCODE", + blockType: Scratch.BlockType.LABEL, + text: "Triangle Blocks", + }, + { + disableMonitor: true, + opcode: "setTriangleFilterMode", + blockType: Scratch.BlockType.COMMAND, + text: "set triangle filter mode to [filter]", + arguments: { + filter: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 9728, + menu: "filterType", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "setTrianglePointAttribute", + blockType: Scratch.BlockType.COMMAND, + text: "set triangle point [point]'s [attribute] to [value]", + arguments: { + point: { + type: Scratch.ArgumentType.STRING, + defaultValue: "1", + menu: "pointMenu", + }, + attribute: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "2", + menu: "triAttribute", + }, + value: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "setWholeTrianglePointAttribute", + blockType: Scratch.BlockType.COMMAND, + text: "set triangle's [wholeAttribute] to [value]", + arguments: { + wholeAttribute: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "2", + menu: "wholeTriAttribute", + }, + value: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "tintTriPoint", + blockType: Scratch.BlockType.COMMAND, + text: "tint triangle point [point] to [color]", + arguments: { + point: { + type: Scratch.ArgumentType.STRING, + defaultValue: "1", + menu: "pointMenu", + }, + color: { + type: Scratch.ArgumentType.COLOR, + defaultValue: "#0000ff", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "tintTri", + blockType: Scratch.BlockType.COMMAND, + text: "tint triangle to [color]", + arguments: { + point: { + type: Scratch.ArgumentType.STRING, + defaultValue: "1", + menu: "pointMenu", + }, + color: { + type: Scratch.ArgumentType.COLOR, + defaultValue: "#0000ff", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "getTrianglePointAttribute", + blockType: Scratch.BlockType.REPORTER, + text: "get triangle point [point]'s [attribute]", + arguments: { + point: { + type: Scratch.ArgumentType.STRING, + defaultValue: "1", + menu: "pointMenu", + }, + attribute: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: 2, + menu: "triAttribute", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "resetWholeTriangleAttributes", + blockType: Scratch.BlockType.COMMAND, + text: "reset triangle attributes", + arguments: {}, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "drawSolidTri", + blockType: Scratch.BlockType.COMMAND, + text: "draw triangle between [x1] [y1], [x2] [y2] and [x3] [y3]", + arguments: { + x1: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + y1: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + x2: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + y2: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + x3: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + y3: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "drawTexTri", + blockType: Scratch.BlockType.COMMAND, + text: "draw textured triangle between [x1] [y1], [x2] [y2] and [x3] [y3] with the texture [tex]", + arguments: { + x1: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + y1: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + x2: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + y2: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + x3: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }, + y3: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + tex: { type: Scratch.ArgumentType.STRING, menu: "costumeMenu" }, + }, + filter: "sprite", + }, + { + opcode: "__NOUSEOPCODE", + blockType: Scratch.BlockType.LABEL, + text: "Color", + }, + { + disableMonitor: true, + opcode: "RGB2HEX", + blockType: Scratch.BlockType.REPORTER, + text: "Red [R] Green [G] Blue [B]", + arguments: { + R: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + G: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + B: { type: Scratch.ArgumentType.NUMBER, defaultValue: 100 }, + }, + }, + { + disableMonitor: true, + opcode: "HSV2RGB", + blockType: Scratch.BlockType.REPORTER, + text: "Hue [H] Saturation [S] Value [V]", + arguments: { + H: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, + S: { type: Scratch.ArgumentType.NUMBER, defaultValue: 100 }, + V: { type: Scratch.ArgumentType.NUMBER, defaultValue: 100 }, + }, + }, + { + opcode: "__NOUSEOPCODE", + blockType: Scratch.BlockType.LABEL, + text: "Images", + }, + { + disableMonitor: true, + opcode: "setDURIclampmode", + blockType: Scratch.BlockType.COMMAND, + text: "set imported image wrap mode to [clampMode]", + arguments: { + clampMode: { + type: Scratch.ArgumentType.STRING, + defaultValue: "33071", + menu: "wrapType", + }, + }, + }, + { + disableMonitor: true, + opcode: "addBlankIMG", + blockType: Scratch.BlockType.COMMAND, + text: "add blank image that is [color] and the size of [width], [height] named [name] to Pen+ Library", + arguments: { + color: { + type: Scratch.ArgumentType.COLOR, + defaultValue: "#ffffff", + }, + width: { type: Scratch.ArgumentType.NUMBER, defaultValue: 128 }, + height: { type: Scratch.ArgumentType.NUMBER, defaultValue: 128 }, + name: { + type: Scratch.ArgumentType.STRING, + defaultValue: "Image", + }, + }, + }, + { + disableMonitor: true, + opcode: "addIMGfromDURI", + blockType: Scratch.BlockType.COMMAND, + text: "add image named [name] from [dataURI] to Pen+ Library", + arguments: { + dataURI: { + type: Scratch.ArgumentType.STRING, + defaultValue: "https://extensions.turbowarp.org/dango.png", + }, + name: { + type: Scratch.ArgumentType.STRING, + defaultValue: "Image", + }, + }, + }, + { + disableMonitor: true, + opcode: "removeIMGfromDURI", + blockType: Scratch.BlockType.COMMAND, + text: "remove image named [name] from Pen+ Library", + arguments: { + name: { + type: Scratch.ArgumentType.STRING, + defaultValue: "Image", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "doesIMGexist", + blockType: Scratch.BlockType.BOOLEAN, + text: "does [name] exist in Pen+ Library", + arguments: { + name: { + type: Scratch.ArgumentType.STRING, + defaultValue: "Image", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "getCostumeDataURI", + blockType: Scratch.BlockType.REPORTER, + text: "get data uri for costume [costume]", + arguments: { + costume: { + type: Scratch.ArgumentType.STRING, + menu: "getCostumeDataURI_costume_Menu", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "getDimensionOf", + blockType: Scratch.BlockType.REPORTER, + text: "get the [dimension] of [costume] in pen+ costume library", + arguments: { + dimension: { + type: Scratch.ArgumentType.STRING, + menu: "getDimensionOf_dimension_Menu", + }, + costume: { + type: Scratch.ArgumentType.STRING, + menu: "penPlusCostumes", + }, + }, + filter: "sprite", + }, + { + disableMonitor: true, + opcode: "setpixelcolor", + blockType: Scratch.BlockType.COMMAND, + text: "set pixel [x] [y]'s color to [color] in [costume]", + arguments: { + x: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + color: { + type: Scratch.ArgumentType.COLOR, + defaultValue: "#0000ff", + }, + costume: { + type: Scratch.ArgumentType.STRING, + menu: "penPlusCostumes", + }, + }, + }, + { + disableMonitor: true, + opcode: "getpixelcolor", + blockType: Scratch.BlockType.REPORTER, + text: "get pixel [x] [y]'s color in [costume]", + arguments: { + x: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 1 }, + costume: { + type: Scratch.ArgumentType.STRING, + menu: "penPlusCostumes", + }, + }, + }, + { + disableMonitor: true, + opcode: "getPenPlusCostumeURI", + blockType: Scratch.BlockType.REPORTER, + text: "get data uri of [costume] in the pen+ costume library", + arguments: { + costume: { + type: Scratch.ArgumentType.STRING, + menu: "penPlusCostumes", + }, + }, + }, + { + opcode: "__NOUSEOPCODE", + blockType: Scratch.BlockType.LABEL, + text: "Advanced options", + }, + { + disableMonitor: true, + opcode: "turnAdvancedSettingOff", + blockType: Scratch.BlockType.COMMAND, + text: "turn advanced setting [Setting] [onOrOff]", + arguments: { + Setting: { + type: Scratch.ArgumentType.STRING, + menu: "advancedSettingsMenu", + }, + onOrOff: { type: Scratch.ArgumentType.STRING, menu: "onOffMenu" }, + }, + }, + { + disableMonitor: true, + opcode: "setAdvancedOptionValueTo", + blockType: Scratch.BlockType.COMMAND, + text: "set [setting] to [value]", + arguments: { + setting: { + type: Scratch.ArgumentType.STRING, + menu: "advancedSettingValuesMenu", + }, + value: { + type: Scratch.ArgumentType.STRING, + defaultValue: "1000", + }, + }, + }, + ], + menus: { + hsvMenu: { + items: [ + "color", + "saturation", + "brightness", + "transparency", + "size", + ], + acceptReporters: true, + }, + stampSquare: { + items: [ + { text: "Width", value: "0" }, + { text: "Height", value: "1" }, + { text: "Rotation", value: "2" }, + { text: "U-Multiplier", value: "3" }, + { text: "U-Offset", value: "4" }, + { text: "V-Multiplier", value: "5" }, + { text: "V-Offset", value: "6" }, + { text: "Red Tint", value: "7" }, + { text: "Green Tint", value: "8" }, + { text: "Blue Tint", value: "9" }, + { text: "Transparency", value: "10" }, + { text: "depth value", value: "11" }, + ], + acceptReporters: true, + }, + triAttribute: { + items: [ + { text: "U value", value: "0" }, + { text: "V value", value: "1" }, + { text: "red tint", value: "2" }, + { text: "green tint", value: "3" }, + { text: "blue tint", value: "4" }, + { text: "transparency", value: "7" }, + { text: "corner pinch", value: "6" }, + { text: "depth value", value: "5" }, + ], + acceptReporters: true, + }, + wholeTriAttribute: { + items: [ + { text: "red tint", value: "2" }, + { text: "green tint", value: "3" }, + { text: "blue tint", value: "4" }, + { text: "transparency", value: "7" }, + { text: "depth value", value: "5" }, + ], + acceptReporters: true, + }, + filterType: { + items: [ + { text: "Closest", value: "9728" }, + { text: "Linear", value: "9729" }, + ], + acceptReporters: true, + }, + wrapType: { + items: [ + { text: "Clamp", value: "33071" }, + { text: "Repeat", value: "10497" }, + { text: "Mirrored", value: "33648" }, + ], + acceptReporters: true, + }, + pointMenu: { items: ["1", "2", "3"], acceptReporters: true }, + onOffMenu: { items: ["on", "off"], acceptReporters: true }, + costumeMenu: { items: "costumeMenuFunction", acceptReporters: true }, + penPlusCostumes: { + items: "penPlusCostumesFunction", + acceptReporters: true, + }, + advancedSettingsMenu: { + items: [ + { text: "allow 'Corner Pinch < 1'", value: "wValueUnderFlow" }, + ], + acceptReporters: true, + }, + advancedSettingValuesMenu: { + items: [{ text: "maximum depth value", value: "depthMax" }], + acceptReporters: false, + }, + getCostumeDataURI_costume_Menu: { + items: "getCostumeDataURI_costume_MenuFunction", + acceptReporters: true, + }, + getDimensionOf_dimension_Menu: { + items: ["width", "height"], + acceptReporters: true, + }, + }, + name: "Pen+", + id: "penP", + menuIconURI: + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMi45OTk3MiIgaGVpZ2h0PSIzMi44ODIwNyIgdmlld0JveD0iMCwwLDMyLjk5OTcyLDMyLjg4MjA3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LC0xNjMuOTk5OTMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIyOC43NTMsMTk0LjYwMmwtNC4yNSwxLjc4bDEuNzgzLC00LjIzN2MxLjIxOCwtMi44OTIgMi45MDcsLTUuNDIzIDUuMDMsLTcuNTM4bDE5Ljc1LC0xOS42NzdjMC44NDYsLTAuODQyIDIuNjUsLTAuNDEgNC4wMzIsMC45NjdjMS4zOCwxLjM3NSAxLjgxNiwzLjE3MyAwLjk3LDQuMDE1bC0xOS43NSwxOS42NzhjLTIuMTIzLDIuMTE2IC00LjY2NCwzLjggLTcuNTY1LDUuMDEyIiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTIzNi44NTgsMTczLjQyOGMwLDAgMi42MTYsMi4yMiA0LjM1LC0xLjU0NmMzLjc1MiwtOC4xNSA4LjIwMiwtNS43NzIgOC4yMDIsLTUuNzcyIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI1Ni40MiwxNjguODI1YzAsMC40NjMgLTAuMTQsMC44NzMgLTAuNDMyLDEuMTY0bC05LjMzNSw5LjNjMC4yODIsLTAuMjkgMC40MSwtMC42NjggMC40MSwtMS4xMmMwLC0wLjg3NCAtMC41MDcsLTEuOTYzIC0xLjQwNiwtMi44NjhjLTEuMzYyLC0xLjM1OCAtMy4xNDcsLTEuOCAtNC4wMDIsLTAuOTlsOS4zMzUsLTkuMzAxYzAuODQ0LC0wLjg0IDIuNjUsLTAuNDEgNC4wMzUsMC45NmMwLjg5OCwwLjkwNCAxLjM5NiwxLjk4MiAxLjM5NiwyLjg1NU0yMzAuNTE1LDE5My43NzRjLTAuNTczLDAuMzAyIC0xLjE1NywwLjU3IC0xLjc2NCwwLjgzbC00LjI1MSwxLjc3OGwxLjc4NiwtNC4yMzVjMC4yNTgsLTAuNjA0IDAuNTMsLTEuMTg2IDAuODMzLC0xLjc1N2MwLjY5LDAuMTgzIDEuNDQ4LDAuNjI1IDIuMTA4LDEuMjgyYzAuNjYsMC42NTggMS4xMDIsMS40MTIgMS4yODcsMi4xMDIiIGZpbGw9IiM0Yzk3ZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjU2LjQ5OCwxNjguNzQ4YzAsMC40NjQgLTAuMTQsMC44NzQgLTAuNDMzLDEuMTY1bC0xOS43NDIsMTkuNjhjLTIuMTMsMi4xMSAtNC42NzMsMy43OTMgLTcuNTcyLDUuMDFsLTQuMjUxLDEuNzc3bDAuOTc0LC0yLjMxNmwxLjkyNSwtMC44MDhjMi44OTgsLTEuMjE4IDUuNDQsLTIuOSA3LjU3LC01LjAxbDE5Ljc0MywtMTkuNjhjMC4yOTIsLTAuMjkyIDAuNDMyLC0wLjcwMiAwLjQzMiwtMS4xNjVjMCwtMC42NDYgLTAuMjcsLTEuNCAtMC43OCwtMi4xMjJjMC4yNSwwLjE3MiAwLjUsMC4zNzcgMC43MzcsMC42MTRjMC44OTgsMC45MDUgMS4zOTYsMS45ODMgMS4zOTYsMi44NTYiIGZpbGw9IiM1NzVlNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIG9wYWNpdHk9IjAuMTUiLz48cGF0aCBkPSJNMjM4LjQ1LDE3Mi44M2MwLDAuNSAtMC40MDQsMC45MDUgLTAuOTA0LDAuOTA1Yy0wLjUsMCAtMC45MDUsLTAuNDA1IC0wLjkwNSwtMC45MDRjMCwtMC41IDAuNDA3LC0wLjkwMyAwLjkwNiwtMC45MDNjMC41LDAgMC45MDQsMC40MDQgMC45MDQsMC45MDR6IiBmaWxsPSIjNTc1ZTc1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI0NC45OTgwNywxODcuMDUyOThoOS41MTc2NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTI0OS43NTY4OSwxOTEuODExOHYtOS41MTc2NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTY6MTYuMDAwMDY5MjMwODQyMTQzLS0+", + blockIconURI: + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==", + docsURI: "https://extensions.turbowarp.org/penplus", + }; + } + costumeMenuFunction() { + const myCostumes = runtime._editingTarget.sprite.costumes; + + let readCostumes = []; + for ( + let curCostumeID = 0; + curCostumeID < myCostumes.length; + curCostumeID++ + ) { + const currentCostume = myCostumes[curCostumeID].name; + readCostumes.push(currentCostume); + } + + const keys = Object.keys(penPlusCostumeLibrary); + if (keys.length > 0) { + for (let curCostumeID = 0; curCostumeID < keys.length; curCostumeID++) { + const currentCostume = keys[curCostumeID]; + readCostumes.push(currentCostume); + } + } + + return readCostumes; + } + penPlusCostumesFunction() { + const readCostumes = []; + const keys = Object.keys(penPlusCostumeLibrary); + if (keys.length > 0) { + for (let curCostumeID = 0; curCostumeID < keys.length; curCostumeID++) { + const currentCostume = keys[curCostumeID]; + readCostumes.push(currentCostume); + } + return readCostumes; + } + + return ["no pen+ costumes!"]; + } + isPenDown(args, util) { + checkForPen(util); + const curTarget = util.target; + return curTarget["_customState"]["Scratch.pen"].penDown; + } + getPenHSV({ HSV }, util) { + checkForPen(util); + const curTarget = util.target; + if (HSV == "size") { + return curTarget["_customState"]["Scratch.pen"].penAttributes.diameter; + } + return curTarget["_customState"]["Scratch.pen"][HSV]; + } + drawDot({ x, y }, util) { + checkForPen(util); + const curTarget = util.target; + const attrib = curTarget["_customState"]["Scratch.pen"].penAttributes; + + curTarget.runtime.ext_pen.penDown(null, util); + + Scratch.vm.renderer.penPoint( + Scratch.vm.renderer._penSkinId, + attrib, + x, + y + ); + + curTarget.runtime.ext_pen.penUp(null, util); + } + drawLine({ x1, y1, x2, y2 }, util) { + checkForPen(util); + const curTarget = util.target; + const attrib = curTarget["_customState"]["Scratch.pen"].penAttributes; + + curTarget.runtime.ext_pen.penDown(null, util); + + Scratch.vm.renderer.penLine( + Scratch.vm.renderer._penSkinId, + attrib, + x1, + y1, + x2, + y2 + ); + + curTarget.runtime.ext_pen.penUp(null, util); + } + squareDown(arg, util) { + //Just a simple thing to allow for pen drawing + const curTarget = util.target; + + checkForPen(util); + + const attrib = curTarget["_customState"]["Scratch.pen"].penAttributes; + const diam = attrib.diameter; + + nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; + + lilPenDabble(nativeSize, curTarget, util); // Do this so the renderer doesn't scream at us + + if ( + typeof triangleAttributesOfAllSprites["squareStamp_" + curTarget.id] == + "undefined" + ) { + triangleAttributesOfAllSprites["squareStamp_" + curTarget.id] = + triangleDefaultAttributes; + } + + if (typeof squareAttributesOfAllSprites[curTarget.id] == "undefined") { + squareAttributesOfAllSprites[curTarget.id] = squareDefaultAttributes; + } + + const myAttributes = squareAttributesOfAllSprites[curTarget.id]; + + //trying my best to reduce memory usage + gl.viewport(0, 0, nativeSize[0], nativeSize[1]); + const dWidth = 1 / nativeSize[0]; + const dHeight = 1 / nativeSize[1]; + + const spritex = curTarget.x; + const spritey = curTarget.y; + + //correction for HQ pen + const typSize = renderer._nativeSize; + const mul = renderer.useHighQualityRender + ? 2 * ((canvas.width + canvas.height) / (typSize[0] + typSize[1])) + : 2; + + //Predifine stuff so there aren't as many calculations + const wMulX = mul * myAttributes[0]; + const wMulY = mul * myAttributes[1]; + + const offDiam = 0.5 * diam; + + const sprXoff = spritex * mul; + const sprYoff = spritey * mul; + //Paratheses because I know some obscure browser will screw this up. + let x1 = Scratch.Cast.toNumber(-offDiam) * wMulX; + let x2 = Scratch.Cast.toNumber(offDiam) * wMulX; + let x3 = Scratch.Cast.toNumber(offDiam) * wMulX; + let x4 = Scratch.Cast.toNumber(-offDiam) * wMulX; + + let y1 = Scratch.Cast.toNumber(offDiam) * wMulY; + let y2 = Scratch.Cast.toNumber(offDiam) * wMulY; + let y3 = Scratch.Cast.toNumber(-offDiam) * wMulY; + let y4 = Scratch.Cast.toNumber(-offDiam) * wMulY; + + function rotateTheThings(ox1, oy1, ox2, oy2, ox3, oy3, ox4, oy4) { + let sin = Math.sin(myAttributes[2] * d2r); + let cos = Math.cos(myAttributes[2] * d2r); + + x1 = ox1 * sin + oy1 * cos; + y1 = ox1 * cos - oy1 * sin; + + x2 = ox2 * sin + oy2 * cos; + y2 = ox2 * cos - oy2 * sin; + + x3 = ox3 * sin + oy3 * cos; + y3 = ox3 * cos - oy3 * sin; + + x4 = ox4 * sin + oy4 * cos; + y4 = ox4 * cos - oy4 * sin; + } + + rotateTheThings(x1, y1, x2, y2, x3, y3, x4, y4); + + x1 += sprXoff; + y1 += sprYoff; + + x2 += sprXoff; + y2 += sprYoff; + + x3 += sprXoff; + y3 += sprYoff; + + x4 += sprXoff; + y4 += sprYoff; + + x1 *= dWidth; + y1 *= dHeight; + + x2 *= dWidth; + y2 *= dHeight; + + x3 *= dWidth; + y3 *= dHeight; + + x4 *= dWidth; + y4 *= dHeight; + + const Attribute_ID = "squareStamp_" + curTarget.id; + + triangleAttributesOfAllSprites[Attribute_ID][2] = myAttributes[7]; + triangleAttributesOfAllSprites[Attribute_ID][3] = myAttributes[8]; + triangleAttributesOfAllSprites[Attribute_ID][4] = myAttributes[9]; + triangleAttributesOfAllSprites[Attribute_ID][5] = myAttributes[11]; + triangleAttributesOfAllSprites[Attribute_ID][7] = myAttributes[10]; + triangleAttributesOfAllSprites[Attribute_ID][10] = myAttributes[7]; + triangleAttributesOfAllSprites[Attribute_ID][11] = myAttributes[8]; + triangleAttributesOfAllSprites[Attribute_ID][12] = myAttributes[9]; + triangleAttributesOfAllSprites[Attribute_ID][13] = myAttributes[11]; + triangleAttributesOfAllSprites[Attribute_ID][15] = myAttributes[10]; + triangleAttributesOfAllSprites[Attribute_ID][18] = myAttributes[7]; + triangleAttributesOfAllSprites[Attribute_ID][19] = myAttributes[8]; + triangleAttributesOfAllSprites[Attribute_ID][20] = myAttributes[9]; + triangleAttributesOfAllSprites[Attribute_ID][21] = myAttributes[11]; + triangleAttributesOfAllSprites[Attribute_ID][23] = myAttributes[10]; + + triFunctions.drawTri( + gl.getParameter(gl.CURRENT_PROGRAM), + x1, + y1, + x2, + y2, + x3, + y3, + attrib.color4f, + Attribute_ID + ); + + triFunctions.drawTri( + gl.getParameter(gl.CURRENT_PROGRAM), + x1, + y1, + x3, + y3, + x4, + y4, + attrib.color4f, + Attribute_ID + ); + } + squareTexDown({ tex }, util) { + //Just a simple thing to allow for pen drawing + const curTarget = util.target; + + let currentTexture = null; + if (penPlusCostumeLibrary[tex]) { + currentTexture = penPlusCostumeLibrary[tex].texture; + } else { + const costIndex = curTarget.getCostumeIndexByName( + Scratch.Cast.toString(tex) + ); + if (costIndex >= 0) { + const curCostume = curTarget.sprite.costumes_[costIndex]; + if (costIndex != curTarget.currentCostume) { + curTarget.setCostume(costIndex); + } + + currentTexture = renderer._allSkins[curCostume.skinId].getTexture(); + } + } + + checkForPen(util); + + const attrib = curTarget["_customState"]["Scratch.pen"].penAttributes; + const diam = attrib.diameter; + + nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; + + lilPenDabble(nativeSize, curTarget, util); // Do this so the renderer doesn't scream at us + + if (!triangleAttributesOfAllSprites["squareStamp_" + curTarget.id]) { + triangleAttributesOfAllSprites["squareStamp_" + curTarget.id] = + triangleDefaultAttributes; + } + + if (!squareAttributesOfAllSprites[curTarget.id]) { + squareAttributesOfAllSprites[curTarget.id] = squareDefaultAttributes; + } + + const myAttributes = squareAttributesOfAllSprites[curTarget.id]; + + //trying my best to reduce memory usage + gl.viewport(0, 0, nativeSize[0], nativeSize[1]); + const dWidth = 1 / nativeSize[0]; + const dHeight = 1 / nativeSize[1]; + + const spritex = curTarget.x; + const spritey = curTarget.y; + + //correction for HQ pen + const typSize = renderer._nativeSize; + const mul = renderer.useHighQualityRender + ? 2 * ((canvas.width + canvas.height) / (typSize[0] + typSize[1])) + : 2; + + //Predifine stuff so there aren't as many calculations + const wMulX = mul * myAttributes[0]; + const wMulY = mul * myAttributes[1]; + + const offDiam = 0.5 * diam; + + const sprXoff = spritex * mul; + const sprYoff = spritey * mul; + //Paratheses because I know some obscure browser will screw this up. + let x1 = Scratch.Cast.toNumber(-offDiam) * wMulX; + let x2 = Scratch.Cast.toNumber(offDiam) * wMulX; + let x3 = Scratch.Cast.toNumber(offDiam) * wMulX; + let x4 = Scratch.Cast.toNumber(-offDiam) * wMulX; + + let y1 = Scratch.Cast.toNumber(offDiam) * wMulY; + let y2 = Scratch.Cast.toNumber(offDiam) * wMulY; + let y3 = Scratch.Cast.toNumber(-offDiam) * wMulY; + let y4 = Scratch.Cast.toNumber(-offDiam) * wMulY; + + function rotateTheThings(ox1, oy1, ox2, oy2, ox3, oy3, ox4, oy4) { + let sin = Math.sin(myAttributes[2] * d2r); + let cos = Math.cos(myAttributes[2] * d2r); + + x1 = ox1 * sin + oy1 * cos; + y1 = ox1 * cos - oy1 * sin; + + x2 = ox2 * sin + oy2 * cos; + y2 = ox2 * cos - oy2 * sin; + + x3 = ox3 * sin + oy3 * cos; + y3 = ox3 * cos - oy3 * sin; + + x4 = ox4 * sin + oy4 * cos; + y4 = ox4 * cos - oy4 * sin; + } + + rotateTheThings(x1, y1, x2, y2, x3, y3, x4, y4); + + x1 += sprXoff; + y1 += sprYoff; + + x2 += sprXoff; + y2 += sprYoff; + + x3 += sprXoff; + y3 += sprYoff; + + x4 += sprXoff; + y4 += sprYoff; + + x1 *= dWidth; + y1 *= dHeight; + + x2 *= dWidth; + y2 *= dHeight; + + x3 *= dWidth; + y3 *= dHeight; + + x4 *= dWidth; + y4 *= dHeight; + + if (currentTexture != null && typeof currentTexture != "undefined") { + const Attribute_ID = "squareStamp_" + curTarget.id; + triangleAttributesOfAllSprites[Attribute_ID][0] = + (0 + myAttributes[4]) * myAttributes[3]; + triangleAttributesOfAllSprites[Attribute_ID][1] = + (1 + myAttributes[6]) * myAttributes[5]; + + triangleAttributesOfAllSprites[Attribute_ID][2] = myAttributes[7]; + triangleAttributesOfAllSprites[Attribute_ID][3] = myAttributes[8]; + triangleAttributesOfAllSprites[Attribute_ID][4] = myAttributes[9]; + triangleAttributesOfAllSprites[Attribute_ID][5] = myAttributes[11]; + triangleAttributesOfAllSprites[Attribute_ID][7] = myAttributes[10]; + + triangleAttributesOfAllSprites[Attribute_ID][8] = + (1 + myAttributes[4]) * myAttributes[3]; + triangleAttributesOfAllSprites[Attribute_ID][9] = + (1 + myAttributes[6]) * myAttributes[5]; + + triangleAttributesOfAllSprites[Attribute_ID][10] = myAttributes[7]; + triangleAttributesOfAllSprites[Attribute_ID][11] = myAttributes[8]; + triangleAttributesOfAllSprites[Attribute_ID][12] = myAttributes[9]; + triangleAttributesOfAllSprites[Attribute_ID][13] = myAttributes[11]; + triangleAttributesOfAllSprites[Attribute_ID][15] = myAttributes[10]; + + triangleAttributesOfAllSprites[Attribute_ID][16] = + (1 + myAttributes[4]) * myAttributes[3]; + triangleAttributesOfAllSprites[Attribute_ID][17] = + (0 + myAttributes[6]) * myAttributes[5]; + + triangleAttributesOfAllSprites[Attribute_ID][18] = myAttributes[7]; + triangleAttributesOfAllSprites[Attribute_ID][19] = myAttributes[8]; + triangleAttributesOfAllSprites[Attribute_ID][20] = myAttributes[9]; + triangleAttributesOfAllSprites[Attribute_ID][21] = myAttributes[11]; + triangleAttributesOfAllSprites[Attribute_ID][23] = myAttributes[10]; + + triFunctions.drawTextTri( + gl.getParameter(gl.CURRENT_PROGRAM), + x1, + y1, + x2, + y2, + x3, + y3, + Attribute_ID, + currentTexture + ); + + triangleAttributesOfAllSprites[Attribute_ID][0] = + (0 + myAttributes[4]) * myAttributes[3]; + triangleAttributesOfAllSprites[Attribute_ID][1] = + (1 + myAttributes[6]) * myAttributes[5]; + + triangleAttributesOfAllSprites[Attribute_ID][8] = + (1 + myAttributes[4]) * myAttributes[3]; + triangleAttributesOfAllSprites[Attribute_ID][9] = + (0 + myAttributes[6]) * myAttributes[5]; + + triangleAttributesOfAllSprites[Attribute_ID][16] = + (0 + myAttributes[4]) * myAttributes[3]; + triangleAttributesOfAllSprites[Attribute_ID][17] = + (0 + myAttributes[6]) * myAttributes[5]; + + triFunctions.drawTextTri( + gl.getParameter(gl.CURRENT_PROGRAM), + x1, + y1, + x3, + y3, + x4, + y4, + Attribute_ID, + currentTexture + ); + } + } + setStampAttribute({ target, number }, util) { + const curTarget = util.target; + if (!squareAttributesOfAllSprites[curTarget.id]) { + squareAttributesOfAllSprites[curTarget.id] = squareDefaultAttributes; + } + + const attributeNum = Scratch.Cast.toNumber(target); + if (attributeNum >= 7) { + if (attributeNum == 11) { + squareAttributesOfAllSprites[curTarget.id][attributeNum] = Math.min( + Math.max(number / penPlusAdvancedSettings._maxDepth, 0), + 1 + ); + return; + } + squareAttributesOfAllSprites[curTarget.id][attributeNum] = + Math.min(Math.max(number, 0), 100) * 0.01; + return; + } + squareAttributesOfAllSprites[curTarget.id][attributeNum] = number; + } + getStampAttribute({ target }, util) { + const curTarget = util.target; + if (!squareAttributesOfAllSprites[curTarget.id]) { + squareAttributesOfAllSprites[curTarget.id] = squareDefaultAttributes; + } + + return squareAttributesOfAllSprites[curTarget.id][ + Scratch.Cast.toNumber(target) + ]; + } + tintSquare({ color }, util) { + const curTarget = util.target; + + if (!squareAttributesOfAllSprites[curTarget.id]) { + squareAttributesOfAllSprites[curTarget.id] = squareDefaultAttributes; + } + + const calcColor = colors.hexToRgb(color); + + squareAttributesOfAllSprites[curTarget.id][7] = calcColor.r / 255; + squareAttributesOfAllSprites[curTarget.id][8] = calcColor.g / 255; + squareAttributesOfAllSprites[curTarget.id][9] = calcColor.b / 255; + } + resetSquareAttributes(args, util) { + const curTarget = util.target; + squareAttributesOfAllSprites[curTarget.id] = [ + 1, 1, 90, 1, 0, 1, 0, 1, 1, 1, 1, 0, + ]; + } + setTriangleFilterMode({ filter }) { + currentFilter = filter; + } + setTrianglePointAttribute({ point, attribute, value }, util) { + const trianglePointStart = (point - 1) * 8; + + const targetId = util.target.id; + + if (!triangleAttributesOfAllSprites[targetId]) { + triangleAttributesOfAllSprites[targetId] = triangleDefaultAttributes; + } + triFunctions.setValueAccordingToCaseTriangle( + targetId, + Scratch.Cast.toNumber(attribute), + value, + false, + trianglePointStart + ); + } + setWholeTrianglePointAttribute({ wholeAttribute, value }, util) { + const targetId = util.target.id; + + if (!triangleAttributesOfAllSprites[targetId]) { + triangleAttributesOfAllSprites[targetId] = triangleDefaultAttributes; + } + triFunctions.setValueAccordingToCaseTriangle( + targetId, + Scratch.Cast.toNumber(wholeAttribute), + value, + true, + 0 + ); + } + tintTriPoint({ point, color }, util) { + const curTarget = util.target; + + const trianglePointStart = (point - 1) * 8; + + const targetId = util.target.id; + + if (!triangleAttributesOfAllSprites[targetId]) { + triangleAttributesOfAllSprites[targetId] = triangleDefaultAttributes; + } + + const calcColor = colors.hexToRgb(color); + + triFunctions.setValueAccordingToCaseTriangle( + targetId, + 2, + calcColor.r / 2.55, + false, + trianglePointStart + ); + + triFunctions.setValueAccordingToCaseTriangle( + targetId, + 3, + calcColor.g / 2.55, + false, + trianglePointStart + ); + + triFunctions.setValueAccordingToCaseTriangle( + targetId, + 4, + calcColor.b / 2.55, + false, + trianglePointStart + ); + } + tintTri({ point, color }, util) { + const curTarget = util.target; + + const trianglePointStart = (point - 1) * 8; + + const targetId = util.target.id; + + if (!triangleAttributesOfAllSprites[targetId]) { + triangleAttributesOfAllSprites[targetId] = triangleDefaultAttributes; + } + + const calcColor = colors.hexToRgb(color); + + triFunctions.setValueAccordingToCaseTriangle( + targetId, + 2, + calcColor.r / 2.55, + true, + trianglePointStart + ); + + triFunctions.setValueAccordingToCaseTriangle( + targetId, + 3, + calcColor.g / 2.55, + true, + trianglePointStart + ); + + triFunctions.setValueAccordingToCaseTriangle( + targetId, + 4, + calcColor.b / 2.55, + true, + trianglePointStart + ); + } + getTrianglePointAttribute({ point, attribute }, util) { + const trianglePointStart = (point - 1) * 8; + + const targetId = util.target.id; + + if (!triangleAttributesOfAllSprites[targetId]) { + triangleAttributesOfAllSprites[targetId] = triangleDefaultAttributes; + } + let value = + triangleAttributesOfAllSprites[targetId][ + trianglePointStart + attribute + ]; + + if ((attribute >= 2 && attribute <= 4) || attribute == 7) { + value *= 100; + } + return value; + } + resetWholeTriangleAttributes(args, util) { + const targetId = util.target.id; + triangleAttributesOfAllSprites[targetId] = [ + 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 1, + ]; + } + drawSolidTri({ x1, y1, x2, y2, x3, y3 }, util) { + const curTarget = util.target; + checkForPen(util); + const attrib = curTarget["_customState"]["Scratch.pen"].penAttributes; + + nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; + + //if (triangleAttributesOfAllSprites[curTarget.id]) { + // triangleAttributesOfAllSprites[curTarget.id][5] = 1; + // triangleAttributesOfAllSprites[curTarget.id][13] = 1; + // triangleAttributesOfAllSprites[curTarget.id][21] = 1; + //} + + //?Renderer Freaks out if we don't do this so do it. + lilPenDabble(nativeSize, curTarget, util); // Do this so the renderer doesn't scream at us + + //trying my best to reduce memory usage + gl.viewport(0, 0, nativeSize[0], nativeSize[1]); + const dWidth = 1 / nativeSize[0]; + const dHeight = 1 / nativeSize[1]; + + //correction for HQ pen + const typSize = renderer._nativeSize; + const mul = renderer.useHighQualityRender + ? 2 * ((canvas.width + canvas.height) / (typSize[0] + typSize[1])) + : 2; + //Paratheses because I know some obscure browser will screw this up. + x1 = Scratch.Cast.toNumber(x1) * dWidth * mul; + x2 = Scratch.Cast.toNumber(x2) * dWidth * mul; + x3 = Scratch.Cast.toNumber(x3) * dWidth * mul; + + y1 = Scratch.Cast.toNumber(y1) * dHeight * mul; + y2 = Scratch.Cast.toNumber(y2) * dHeight * mul; + y3 = Scratch.Cast.toNumber(y3) * dHeight * mul; + + triFunctions.drawTri( + gl.getParameter(gl.CURRENT_PROGRAM), + x1, + y1, + x2, + y2, + x3, + y3, + attrib.color4f, + curTarget.id + ); + } + drawTexTri({ x1, y1, x2, y2, x3, y3, tex }, util) { + const curTarget = util.target; + let currentTexture = null; + if (penPlusCostumeLibrary[tex]) { + currentTexture = penPlusCostumeLibrary[tex].texture; + } else { + const costIndex = curTarget.getCostumeIndexByName( + Scratch.Cast.toString(tex) + ); + if (costIndex >= 0) { + const curCostume = curTarget.sprite.costumes_[costIndex]; + if (costIndex != curTarget.currentCostume) { + curTarget.setCostume(costIndex); + } + + currentTexture = renderer._allSkins[curCostume.skinId].getTexture(); + } + } + + nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; + + //?Renderer Freaks out if we don't do this so do it. + lilPenDabble(nativeSize, curTarget, util); // Do this so the renderer doesn't scream at us + + //trying my best to reduce memory usage + gl.viewport(0, 0, nativeSize[0], nativeSize[1]); + const dWidth = 1 / nativeSize[0]; + const dHeight = 1 / nativeSize[1]; + + //correction for HQ pen + const typSize = renderer._nativeSize; + const mul = renderer.useHighQualityRender + ? 2 * ((canvas.width + canvas.height) / (typSize[0] + typSize[1])) + : 2; + //Paratheses because I know some obscure browser will screw this up. + x1 = Scratch.Cast.toNumber(x1) * dWidth * mul; + x2 = Scratch.Cast.toNumber(x2) * dWidth * mul; + x3 = Scratch.Cast.toNumber(x3) * dWidth * mul; + + y1 = Scratch.Cast.toNumber(y1) * dHeight * mul; + y2 = Scratch.Cast.toNumber(y2) * dHeight * mul; + y3 = Scratch.Cast.toNumber(y3) * dHeight * mul; + + if (currentTexture != null && typeof currentTexture != "undefined") { + triFunctions.drawTextTri( + gl.getParameter(gl.CURRENT_PROGRAM), + x1, + y1, + x2, + y2, + x3, + y3, + curTarget.id, + currentTexture + ); + } + } + RGB2HEX({ R, G, B }) { + return colors.rgbtoSColor({ R: R, G: G, B: B }); + } + HSV2RGB({ H, S, V }) { + S = S / 100; + V = V / 100; + S = Math.min(Math.max(S, 0), 1); + V = Math.min(Math.max(V, 0), 1); + H = H % 360; + const C = V * S; + const X = C * (1 - Math.abs(((H / 60) % 2) - 1)); + const M = V - C; + let Primes = [0, 0, 0]; + if (H >= 0 && H < 60) { + Primes[0] = C; + Primes[1] = X; + } else if (H >= 60 && H < 120) { + Primes[0] = X; + Primes[1] = C; + } else if (H >= 120 && H < 180) { + Primes[1] = C; + Primes[2] = X; + } else if (H >= 180 && H < 240) { + Primes[1] = X; + Primes[2] = C; + } else if (H >= 240 && H < 300) { + Primes[0] = X; + Primes[2] = C; + } + if (H >= 300 && H < 360) { + Primes[0] = C; + Primes[2] = X; + } + Primes[0] = (Primes[0] + M) * 255; + Primes[1] = (Primes[1] + M) * 255; + Primes[2] = (Primes[2] + M) * 255; + return colors.rgbtoSColor({ + R: Primes[0] / 2.55, + G: Primes[1] / 2.55, + B: Primes[2] / 2.55, + }); + } + setDURIclampmode({ clampMode }) { + penPlusImportWrapMode = clampMode; + } + addBlankIMG({ color, width, height, name }) { + //Just a simple thing to allow for pen drawing + textureFunctions.createBlankPenPlusTextureInfo( + width, + height, + color, + "!" + name, + penPlusImportWrapMode + ); + } + addIMGfromDURI({ dataURI, name }) { + //Just a simple thing to allow for pen drawing + textureFunctions.createPenPlusTextureInfo( + dataURI, + "!" + name, + penPlusImportWrapMode + ); + } + removeIMGfromDURI({ name }, util) { + //Just a simple thing to allow for pen drawing + if (penPlusCostumeLibrary["!" + name]) { + delete penPlusCostumeLibrary["!" + name]; + } + } + doesIMGexist({ name }, util) { + //Just a simple thing to allow for pen drawing + return typeof penPlusCostumeLibrary["!" + name] != "undefined"; + } + getCostumeDataURI({ costume }, util) { + //Just a simple thing to allow for pen drawing + const curTarget = util.target; + const costIndex = curTarget.getCostumeIndexByName( + Scratch.Cast.toString(costume) + ); + if (costIndex >= 0) { + const curCostume = + curTarget.sprite.costumes_[costIndex].asset.encodeDataURI(); + return curCostume; + } + } + getCostumeDataURI_costume_MenuFunction() { + const myCostumes = runtime._editingTarget.sprite.costumes; + + let readCostumes = []; + for ( + let curCostumeID = 0; + curCostumeID < myCostumes.length; + curCostumeID++ + ) { + const currentCostume = myCostumes[curCostumeID].name; + readCostumes.push(currentCostume); + } + + return readCostumes; + } + getDimensionOf({ dimension, costume }, util) { + //Just a simple thing to allow for pen drawing + const costIndex = penPlusCostumeLibrary[costume]; + if (costIndex) { + return costIndex[dimension]; + } + } + setpixelcolor({ x, y, color, costume }) { + const curCostume = penPlusCostumeLibrary[costume]; + if (curCostume) { + const textureData = textureFunctions.getTextureData( + curCostume.texture, + curCostume.width, + curCostume.height + ); + if (textureData) { + x = Math.floor(x - 1); + y = Math.floor(y - 1); + const colorIndex = (y * curCostume.width + x) * 4; + if ( + textureData[colorIndex] != undefined && + x < curCostume.width && + x >= 0 + ) { + const retColor = colors.hexToRgb(color); + textureData[colorIndex] = retColor.r; + textureData[colorIndex + 1] = retColor.g; + textureData[colorIndex + 2] = retColor.b; + textureData[colorIndex + 3] = 255; + + gl.bindTexture(gl.TEXTURE_2D, curCostume.texture); + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + curCostume.width, + curCostume.height, + 0, + gl.RGBA, + gl.UNSIGNED_BYTE, + textureData + ); + } + } + } + } + getpixelcolor({ x, y, costume }) { + const curCostume = penPlusCostumeLibrary[costume]; + if (curCostume) { + const textureData = textureFunctions.getTextureData( + curCostume.texture, + curCostume.width, + curCostume.height + ); + if (textureData) { + x = Math.floor(x - 1); + y = Math.floor(y - 1); + const colorIndex = (y * curCostume.width + x) * 4; + if (textureData[colorIndex] && x < curCostume.width && x >= 0) { + return colors.rgbtoSColor({ + R: textureData[colorIndex] / 2.55, + G: textureData[colorIndex + 1] / 2.55, + B: textureData[colorIndex + 2] / 2.55, + }); + } + return colors.rgbtoSColor({ R: 100, G: 100, B: 100 }); + } + } + } + getPenPlusCostumeURI({ costume }) { + const curCostume = penPlusCostumeLibrary[costume]; + if (curCostume) { + const textureData = textureFunctions.getTextureAsURI( + curCostume.texture, + curCostume.width, + curCostume.height + ); + if (textureData) { + return textureData; + } + return ""; + } + } + turnAdvancedSettingOff({ Setting, onOrOff }) { + if (onOrOff == "on") { + penPlusAdvancedSettings[Setting] = true; + return; + } + penPlusAdvancedSettings[Setting] = false; + } + setAdvancedOptionValueTo({ setting, value }) { + switch (setting) { + case "depthMax": + penPlusAdvancedSettings._maxDepth = Math.max(value, 100); + break; + + default: + break; + } + } + runtime = Scratch.vm.runtime; + } + + if (!Scratch.vm.renderer._penSkinId) { + Scratch.vm.renderer.createPenSkin(); + } + renderer.penClear(Scratch.vm.renderer._penSkinId); + Scratch.vm.renderer.penLine( + Scratch.vm.renderer._penSkinId, + { + color4f: [0, 0, 1, 1], + diameter: 1, + }, + 0, + 0, + 0, + 0 + ); + + penPlusShaders.pen.program = shaderManager._shaderCache.line[0].program; + + Scratch.extensions.register(new extension()); +})(Scratch); From cf73cad6e3a0193817da7ccf9ded9f95c7262149 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:23:46 -0400 Subject: [PATCH 02/14] Update penPlus.js --- extensions/obviousAlexC/penPlus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index c3866f2695..7af95a70ff 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -918,7 +918,7 @@ }; const textureFunctions = { - createBlankPenPlusTextureInfo: async function ( + createBlankPenPlusTextureInfo: function ( width, height, color, From bafc9ddd5ed37d10d5c53eaab8cf20e3155d6b82 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:28:45 -0400 Subject: [PATCH 03/14] Final patch --- extensions/obviousAlexC/penPlus.js | 311 +++++++++++++++++------------ 1 file changed, 179 insertions(+), 132 deletions(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index 7af95a70ff..de50d96cfd 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -94,7 +94,6 @@ ); gl.enable(gl.DEPTH_TEST); - gl.depthFunc(gl.LEQUAL); gl.bindFramebuffer(gl.FRAMEBUFFER, lastFB); @@ -152,16 +151,15 @@ updateCanvasSize(); }); - let lastURL = window.location.href; - let lastHQPen = renderer.useHighQualityRender; - vm.runtime.on("BEFORE_EXECUTE", () => { if ( - lastURL != window.location.href || - lastHQPen != renderer.useHighQualityRender + (renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize) != nativeSize ) { - lastURL = window.location.href; - lastHQPen = renderer.useHighQualityRender; + nativeSize = renderer.useHighQualityRender + ? [canvas.width, canvas.height] + : renderer._nativeSize; updateCanvasSize(); } }); @@ -223,116 +221,140 @@ untextured: { Shaders: { vert: ` - attribute highp vec4 a_position; - attribute highp vec4 a_color; - varying vec4 v_color; - - varying highp float v_depth; - - void main() - { + attribute highp vec4 a_position; + attribute highp vec4 a_color; + varying highp vec4 v_color; + + varying highp float v_depth; + + void main() + { v_color = a_color; v_depth = a_position.z; gl_Position = a_position * vec4(a_position.w,a_position.w,0,1); - } - `, + } + `, frag: ` - varying highp vec4 v_color; - - uniform mediump vec2 u_res; - uniform sampler2D u_depthTexture; - - varying highp float v_depth; - - void main() - { - gl_FragColor = v_color; - highp vec4 v_depthPart = texture2D(u_depthTexture,gl_FragCoord.xy/u_res); - highp float v_depthcalc = (v_depthPart.r+v_depthPart.g+v_depthPart.b)/3.0; - if (v_depthcalc < v_depth){ - gl_FragColor.a = 0.0; - } - gl_FragColor.rgb *= gl_FragColor.a; + varying highp vec4 v_color; + + uniform mediump vec2 u_res; + uniform sampler2D u_depthTexture; + + varying highp float v_depth; + + void main() + { + gl_FragColor = v_color; + highp vec4 v_depthPart = texture2D(u_depthTexture,gl_FragCoord.xy/u_res); + highp float v_depthcalc = v_depthPart.r + floor((v_depthPart.g + floor(v_depthPart.b * 100.0 )) * 100.0); + + highp float v_inDepth = v_depth; + + if (v_depth < 0.0 ) { + v_inDepth = 0.0; + } + if (v_depth > 10000.0 ) { + v_inDepth = 10000.0; + } + + if (v_depthcalc <= v_inDepth){ + gl_FragColor.a = 0.0; } - `, + + gl_FragColor.rgb *= gl_FragColor.a; + } + `, }, ProgramInf: null, }, textured: { Shaders: { vert: ` - attribute highp vec4 a_position; - attribute highp vec4 a_color; - attribute highp vec2 a_texCoord; - - varying highp vec4 v_color; - varying highp vec2 v_texCoord; - - varying highp float v_depth; - - void main() - { - v_color = a_color; - v_texCoord = a_texCoord; - v_depth = a_position.z * 3.0; - gl_Position = a_position * vec4(a_position.w,a_position.w,0,1); - } - `, + attribute highp vec4 a_position; + attribute highp vec4 a_color; + attribute highp vec2 a_texCoord; + + varying highp vec4 v_color; + varying highp vec2 v_texCoord; + + varying highp float v_depth; + + void main() + { + v_color = a_color; + v_texCoord = a_texCoord; + v_depth = a_position.z; + gl_Position = a_position * vec4(a_position.w,a_position.w,0,1); + } + `, frag: ` - uniform sampler2D u_texture; - - varying highp vec2 v_texCoord; - varying highp vec4 v_color; - - uniform mediump vec2 u_res; - uniform sampler2D u_depthTexture; - - varying highp float v_depth; - - void main() - { - gl_FragColor = texture2D(u_texture, v_texCoord) * v_color; - highp vec4 v_depthPart = texture2D(u_depthTexture,gl_FragCoord.xy/u_res); - highp float v_depthcalc = (v_depthPart.r+v_depthPart.g+v_depthPart.b); - if (v_depthcalc < v_depth){ - gl_FragColor.a = 0.0; - } - gl_FragColor.rgb *= gl_FragColor.a; - - } - `, + uniform sampler2D u_texture; + + varying highp vec2 v_texCoord; + varying highp vec4 v_color; + + uniform mediump vec2 u_res; + uniform sampler2D u_depthTexture; + + varying highp float v_depth; + + void main() + { + gl_FragColor = texture2D(u_texture, v_texCoord) * v_color; + highp vec4 v_depthPart = texture2D(u_depthTexture,gl_FragCoord.xy/u_res); + highp float v_depthcalc = v_depthPart.r + floor((v_depthPart.g + floor(v_depthPart.b * 100.0 )) * 100.0); + + highp float v_inDepth = v_depth; + + if (v_depth < 0.0 ) { + v_inDepth = 0.0; + } + if (v_depth > 10000.0 ) { + v_inDepth = 10000.0; + } + + if (v_depthcalc <= v_inDepth){ + gl_FragColor.a = 0.0; + } + + gl_FragColor.rgb *= gl_FragColor.a; + + } + `, }, ProgramInf: null, }, depth: { Shaders: { vert: ` - attribute highp vec4 a_position; - - varying highp float v_depth; - - void main() - { - v_depth = a_position.z*3.0; - gl_Position = a_position * vec4(a_position.w,a_position.w,a_position.w,1); - } - `, + attribute highp vec4 a_position; + + varying highp float v_depth; + + void main() + { + v_depth = a_position.z; + gl_Position = a_position * vec4(a_position.w,a_position.w,a_position.w * 0.0001,1); + } + `, frag: ` - varying highp float v_depth; - - void main() - { - gl_FragColor = vec4(v_depth,0,0,1); - if (v_depth>1.0) { - gl_FragColor.r = 1.0; - gl_FragColor.g = v_depth-1.0; - if (v_depth>2.0) { - gl_FragColor.g = 1.0; - gl_FragColor.b = v_depth-2.0; - } - } - } - `, + varying highp float v_depth; + + void main() + { + if (v_depth >= 10000.0) { + gl_FragColor = vec4(1,1,1,1); + } + else { + highp float d_100 = floor(v_depth / 100.0); + gl_FragColor = vec4( + mod(v_depth,1.0), + mod( floor( v_depth - mod(v_depth,1.0) )/100.0,1.0), + mod( floor( d_100 - mod(d_100,1.0) )/100.0,1.0), + 1); + } + } + `, }, ProgramInf: null, }, @@ -507,6 +529,8 @@ //I plan to add more later const penPlusAdvancedSettings = { wValueUnderFlow: false, + useDepthBuffer: true, + _ClampZ: false, _maxDepth: 1000, }; @@ -608,7 +632,9 @@ gl.drawArrays(gl.TRIANGLES, 0, 3); //? Hacky fix but it works. - triFunctions.drawDepthTri(targetID, x1, y1, x2, y2, x3, y3); + if (penPlusAdvancedSettings.useDepthBuffer) { + triFunctions.drawDepthTri(targetID, x1, y1, x2, y2, x3, y3); + } gl.useProgram(penPlusShaders.pen.program); }, @@ -620,7 +646,7 @@ vertexBufferData = new Float32Array([ x1, -y1, - triAttribs[5], + penPlusAdvancedSettings.useDepthBuffer ? triAttribs[5] : 0, triAttribs[6], triAttribs[2], triAttribs[3], @@ -631,7 +657,7 @@ x2, -y2, - triAttribs[13], + penPlusAdvancedSettings.useDepthBuffer ? triAttribs[13] : 0, triAttribs[14], triAttribs[10], triAttribs[11], @@ -642,7 +668,7 @@ x3, -y3, - triAttribs[21], + penPlusAdvancedSettings.useDepthBuffer ? triAttribs[21] : 0, triAttribs[22], triAttribs[18], triAttribs[19], @@ -655,7 +681,7 @@ vertexBufferData = new Float32Array([ x1, -y1, - 1, + 0, 1, 1, 1, @@ -666,7 +692,7 @@ x2, -y2, - 1, + 0, 1, 1, 1, @@ -677,7 +703,7 @@ x3, -y3, - 1, + 0, 1, 1, 1, @@ -728,8 +754,9 @@ gl.uniform2fv(u_res_Location_text, nativeSize); gl.drawArrays(gl.TRIANGLES, 0, 3); - //? Hacky fix but it works. - triFunctions.drawDepthTri(targetID, x1, y1, x2, y2, x3, y3); + if (penPlusAdvancedSettings.useDepthBuffer) { + triFunctions.drawDepthTri(targetID, x1, y1, x2, y2, x3, y3); + } gl.useProgram(penPlusShaders.pen.program); }, @@ -833,11 +860,20 @@ //Clamp to 0 so we can't go behind the stage. //Z case 5: - if (value < 0) { - valuetoSet = 0; + if (penPlusAdvancedSettings._ClampZ) { + if (value < 0) { + valuetoSet = 0; + break; + } + //convert to depth space for best accuracy + valuetoSet = Math.min( + (value * 10000) / penPlusAdvancedSettings._maxDepth, + 10000 + ); break; } - valuetoSet = Math.min(value / penPlusAdvancedSettings._maxDepth, 1); + //convert to depth space for best accuracy + valuetoSet = (value * 10000) / penPlusAdvancedSettings._maxDepth; break; //Clamp to 1 so we don't accidentally clip. @@ -918,7 +954,7 @@ }; const textureFunctions = { - createBlankPenPlusTextureInfo: function ( + createBlankPenPlusTextureInfo: async function ( width, height, color, @@ -1687,6 +1723,8 @@ advancedSettingsMenu: { items: [ { text: "allow 'Corner Pinch < 1'", value: "wValueUnderFlow" }, + { text: "toggle depth buffer", value: "useDepthBuffer" }, + { text: "clamp depth value", value: "_ClampZ" }, ], acceptReporters: true, }, @@ -2145,10 +2183,16 @@ const attributeNum = Scratch.Cast.toNumber(target); if (attributeNum >= 7) { if (attributeNum == 11) { - squareAttributesOfAllSprites[curTarget.id][attributeNum] = Math.min( - Math.max(number / penPlusAdvancedSettings._maxDepth, 0), - 1 - ); + if (penPlusAdvancedSettings._ClampZ) { + Math.min( + Math.max(number / penPlusAdvancedSettings._maxDepth, 0), + 1 + ); + return; + } + valuetoSet = value / penPlusAdvancedSettings._maxDepth; + squareAttributesOfAllSprites[curTarget.id][attributeNum] = + number / penPlusAdvancedSettings._maxDepth; return; } squareAttributesOfAllSprites[curTarget.id][attributeNum] = @@ -2629,23 +2673,26 @@ runtime = Scratch.vm.runtime; } - if (!Scratch.vm.renderer._penSkinId) { - Scratch.vm.renderer.createPenSkin(); - } - renderer.penClear(Scratch.vm.renderer._penSkinId); - Scratch.vm.renderer.penLine( - Scratch.vm.renderer._penSkinId, - { - color4f: [0, 0, 1, 1], - diameter: 1, - }, - 0, - 0, - 0, - 0 - ); + //? A small hack to stop the renderer from immediatly dying. And to allow for immediate use + { + if (!Scratch.vm.renderer._penSkinId) { + window.vm.renderer.createPenSkin(); + } + renderer.penClear(Scratch.vm.renderer._penSkinId); + Scratch.vm.renderer.penLine( + Scratch.vm.renderer._penSkinId, + { + color4f: [0, 0, 1, 1], + diameter: 1, + }, + 0, + 0, + 0, + 0 + ); - penPlusShaders.pen.program = shaderManager._shaderCache.line[0].program; + penPlusShaders.pen.program = shaderManager._shaderCache.line[0].program; + } Scratch.extensions.register(new extension()); })(Scratch); From de961264422300386631121679b878a63da3fda9 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:31:08 -0400 Subject: [PATCH 04/14] Update penPlus.js --- extensions/obviousAlexC/penPlus.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index de50d96cfd..19799bfce5 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -1,3 +1,8 @@ +// Name: Pen Plus +// ID: penP +// Description: Advanced rendering capabilities. +// By: ObviousAlexC + (function (Scratch) { "use strict"; From 924734c9656a3ba2d876609e373367ad946443c2 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:36:36 -0400 Subject: [PATCH 05/14] Update penplus.js --- extensions/penplus.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/penplus.js b/extensions/penplus.js index 2da667498c..cad0671189 100644 --- a/extensions/penplus.js +++ b/extensions/penplus.js @@ -1,8 +1,3 @@ -// Name: Pen Plus -// ID: betterpen -// Description: Advanced rendering capabilities. -// By: ObviousAlexC - /* eslint-disable no-empty-pattern */ /* eslint-disable no-prototype-builtins */ /* From 122ff91771220cb2c662d48501cf9365b91cd861 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 00:59:31 -0400 Subject: [PATCH 06/14] Update penPlus.js --- extensions/obviousAlexC/penPlus.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index 19799bfce5..fee92f30f8 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -959,7 +959,7 @@ }; const textureFunctions = { - createBlankPenPlusTextureInfo: async function ( + createBlankPenPlusTextureInfo: function ( width, height, color, @@ -1004,7 +1004,7 @@ height: height, }; }, - createPenPlusTextureInfo: async function (url, name, clamp) { + createPenPlusTextureInfo: function (url, name, clamp) { const texture = penPlusCostumeLibrary[name] ? penPlusCostumeLibrary[name].texture : gl.createTexture(); From b3289fe9a19396aa569fb41c292a6a69d5720480 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 01:01:12 -0400 Subject: [PATCH 07/14] Update penplus.js --- extensions/penplus.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/penplus.js b/extensions/penplus.js index cad0671189..ddb41280a9 100644 --- a/extensions/penplus.js +++ b/extensions/penplus.js @@ -1,3 +1,8 @@ +// Name: Old Pen Plus +// ID: betterpen +// Description: Advanced rendering capabilities. +// By: ObviousAlexC + /* eslint-disable no-empty-pattern */ /* eslint-disable no-prototype-builtins */ /* From be438e757c2ccbc6746b3224a5782ad035e99c7d Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 01:01:32 -0400 Subject: [PATCH 08/14] Update penPlus.js --- extensions/obviousAlexC/penPlus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index fee92f30f8..81be603e6a 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -1,6 +1,6 @@ // Name: Pen Plus // ID: penP -// Description: Advanced rendering capabilities. +// Description: Various Additions to the pen. // By: ObviousAlexC (function (Scratch) { From bbae196651a628a53ff257ab8b15af9c3a515d48 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:56:18 -0400 Subject: [PATCH 09/14] Update penPlus.js --- extensions/obviousAlexC/penPlus.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index 81be603e6a..d487ae63bb 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -2,7 +2,6 @@ // ID: penP // Description: Various Additions to the pen. // By: ObviousAlexC - (function (Scratch) { "use strict"; @@ -959,7 +958,7 @@ }; const textureFunctions = { - createBlankPenPlusTextureInfo: function ( + createBlankPenPlusTextureInfo: async function ( width, height, color, @@ -1004,7 +1003,7 @@ height: height, }; }, - createPenPlusTextureInfo: function (url, name, clamp) { + createPenPlusTextureInfo: async function (url, name, clamp) { const texture = penPlusCostumeLibrary[name] ? penPlusCostumeLibrary[name].texture : gl.createTexture(); @@ -2185,6 +2184,8 @@ squareAttributesOfAllSprites[curTarget.id] = squareDefaultAttributes; } + let valuetoSet = 0; + const attributeNum = Scratch.Cast.toNumber(target); if (attributeNum >= 7) { if (attributeNum == 11) { @@ -2195,7 +2196,7 @@ ); return; } - valuetoSet = value / penPlusAdvancedSettings._maxDepth; + valuetoSet = number / penPlusAdvancedSettings._maxDepth; squareAttributesOfAllSprites[curTarget.id][attributeNum] = number / penPlusAdvancedSettings._maxDepth; return; From c905e6fe885a837173c2c8c6986e14906ed3dd48 Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:57:53 -0400 Subject: [PATCH 10/14] Update penPlus.js --- extensions/obviousAlexC/penPlus.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index d487ae63bb..d21da5d65b 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -958,7 +958,7 @@ }; const textureFunctions = { - createBlankPenPlusTextureInfo: async function ( + createBlankPenPlusTextureInfo: function ( width, height, color, @@ -1003,7 +1003,7 @@ height: height, }; }, - createPenPlusTextureInfo: async function (url, name, clamp) { + createPenPlusTextureInfo: function (url, name, clamp) { const texture = penPlusCostumeLibrary[name] ? penPlusCostumeLibrary[name].texture : gl.createTexture(); From c2e15d4ddfa4500b1a9b3ff72d9619cce336241f Mon Sep 17 00:00:00 2001 From: Obvious Alex C <76855369+David-Orangemoon@users.noreply.github.com> Date: Sun, 27 Aug 2023 14:30:39 -0400 Subject: [PATCH 11/14] Fixes --- extensions/obviousAlexC/penPlus.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index d21da5d65b..a373e24534 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -261,7 +261,7 @@ v_inDepth = 10000.0; } - if (v_depthcalc <= v_inDepth){ + if (v_depthcalc < v_inDepth){ gl_FragColor.a = 0.0; } @@ -317,7 +317,7 @@ v_inDepth = 10000.0; } - if (v_depthcalc <= v_inDepth){ + if (v_depthcalc < v_inDepth){ gl_FragColor.a = 0.0; } From 1ad28cd7c5e759cdc61ae74bd13bbbec513d90f1 Mon Sep 17 00:00:00 2001 From: Muffin Date: Sun, 5 Nov 2023 17:56:49 -0600 Subject: [PATCH 12/14] I'm sorry for the delay! --- extensions/extensions.json | 1 + extensions/obviousAlexC/penPlus.js | 10 +++++----- extensions/penplus.js | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/extensions/extensions.json b/extensions/extensions.json index 73ce264655..b78799cf2b 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -17,6 +17,7 @@ "sound", "Xeltalliv/clippingblending", "clipboard", + "obviousAlexC/penPlus", "penplus", "Lily/Skins", "obviousAlexC/SensingPlus", diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index a373e24534..8bc3317387 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -1,7 +1,8 @@ -// Name: Pen Plus +// Name: Pen Plus V6 // ID: penP -// Description: Various Additions to the pen. +// Description: Advanced rendering capabilities. // By: ObviousAlexC + (function (Scratch) { "use strict"; @@ -1745,13 +1746,12 @@ acceptReporters: true, }, }, - name: "Pen+", + name: "Pen+ V6", id: "penP", menuIconURI: "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMi45OTk3MiIgaGVpZ2h0PSIzMi44ODIwNyIgdmlld0JveD0iMCwwLDMyLjk5OTcyLDMyLjg4MjA3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LC0xNjMuOTk5OTMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIyOC43NTMsMTk0LjYwMmwtNC4yNSwxLjc4bDEuNzgzLC00LjIzN2MxLjIxOCwtMi44OTIgMi45MDcsLTUuNDIzIDUuMDMsLTcuNTM4bDE5Ljc1LC0xOS42NzdjMC44NDYsLTAuODQyIDIuNjUsLTAuNDEgNC4wMzIsMC45NjdjMS4zOCwxLjM3NSAxLjgxNiwzLjE3MyAwLjk3LDQuMDE1bC0xOS43NSwxOS42NzhjLTIuMTIzLDIuMTE2IC00LjY2NCwzLjggLTcuNTY1LDUuMDEyIiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTIzNi44NTgsMTczLjQyOGMwLDAgMi42MTYsMi4yMiA0LjM1LC0xLjU0NmMzLjc1MiwtOC4xNSA4LjIwMiwtNS43NzIgOC4yMDIsLTUuNzcyIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI1Ni40MiwxNjguODI1YzAsMC40NjMgLTAuMTQsMC44NzMgLTAuNDMyLDEuMTY0bC05LjMzNSw5LjNjMC4yODIsLTAuMjkgMC40MSwtMC42NjggMC40MSwtMS4xMmMwLC0wLjg3NCAtMC41MDcsLTEuOTYzIC0xLjQwNiwtMi44NjhjLTEuMzYyLC0xLjM1OCAtMy4xNDcsLTEuOCAtNC4wMDIsLTAuOTlsOS4zMzUsLTkuMzAxYzAuODQ0LC0wLjg0IDIuNjUsLTAuNDEgNC4wMzUsMC45NmMwLjg5OCwwLjkwNCAxLjM5NiwxLjk4MiAxLjM5NiwyLjg1NU0yMzAuNTE1LDE5My43NzRjLTAuNTczLDAuMzAyIC0xLjE1NywwLjU3IC0xLjc2NCwwLjgzbC00LjI1MSwxLjc3OGwxLjc4NiwtNC4yMzVjMC4yNTgsLTAuNjA0IDAuNTMsLTEuMTg2IDAuODMzLC0xLjc1N2MwLjY5LDAuMTgzIDEuNDQ4LDAuNjI1IDIuMTA4LDEuMjgyYzAuNjYsMC42NTggMS4xMDIsMS40MTIgMS4yODcsMi4xMDIiIGZpbGw9IiM0Yzk3ZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjU2LjQ5OCwxNjguNzQ4YzAsMC40NjQgLTAuMTQsMC44NzQgLTAuNDMzLDEuMTY1bC0xOS43NDIsMTkuNjhjLTIuMTMsMi4xMSAtNC42NzMsMy43OTMgLTcuNTcyLDUuMDFsLTQuMjUxLDEuNzc3bDAuOTc0LC0yLjMxNmwxLjkyNSwtMC44MDhjMi44OTgsLTEuMjE4IDUuNDQsLTIuOSA3LjU3LC01LjAxbDE5Ljc0MywtMTkuNjhjMC4yOTIsLTAuMjkyIDAuNDMyLC0wLjcwMiAwLjQzMiwtMS4xNjVjMCwtMC42NDYgLTAuMjcsLTEuNCAtMC43OCwtMi4xMjJjMC4yNSwwLjE3MiAwLjUsMC4zNzcgMC43MzcsMC42MTRjMC44OTgsMC45MDUgMS4zOTYsMS45ODMgMS4zOTYsMi44NTYiIGZpbGw9IiM1NzVlNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIG9wYWNpdHk9IjAuMTUiLz48cGF0aCBkPSJNMjM4LjQ1LDE3Mi44M2MwLDAuNSAtMC40MDQsMC45MDUgLTAuOTA0LDAuOTA1Yy0wLjUsMCAtMC45MDUsLTAuNDA1IC0wLjkwNSwtMC45MDRjMCwtMC41IDAuNDA3LC0wLjkwMyAwLjkwNiwtMC45MDNjMC41LDAgMC45MDQsMC40MDQgMC45MDQsMC45MDR6IiBmaWxsPSIjNTc1ZTc1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI0NC45OTgwNywxODcuMDUyOThoOS41MTc2NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTI0OS43NTY4OSwxOTEuODExOHYtOS41MTc2NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTY6MTYuMDAwMDY5MjMwODQyMTQzLS0+", blockIconURI: - "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==", - docsURI: "https://extensions.turbowarp.org/penplus", + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==" }; } costumeMenuFunction() { diff --git a/extensions/penplus.js b/extensions/penplus.js index ddb41280a9..1b7e3e6010 100644 --- a/extensions/penplus.js +++ b/extensions/penplus.js @@ -1,6 +1,6 @@ -// Name: Old Pen Plus +// Name: Pen Plus V5 (Old) // ID: betterpen -// Description: Advanced rendering capabilities. +// Description: Replaced by Pen Plus V6. // By: ObviousAlexC /* eslint-disable no-empty-pattern */ @@ -2465,7 +2465,7 @@ Other various small fixes getInfo() { return { id: "betterpen", - name: "Pen+", + name: "Pen+ V5", color1: "#0e9a6b", color2: "#0b7f58", color3: "#096647", From 6ca51db7c49afda16771c097480a5c454ecde868 Mon Sep 17 00:00:00 2001 From: Muffin Date: Sun, 5 Nov 2023 17:58:27 -0600 Subject: [PATCH 13/14] Update some block names --- extensions/obviousAlexC/penPlus.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index 8bc3317387..11ee1c5152 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -1259,7 +1259,7 @@ disableMonitor: true, opcode: "getStampAttribute", blockType: Scratch.BlockType.REPORTER, - text: "Get pen square's [target]", + text: "get pen square's [target]", arguments: { target: { type: Scratch.ArgumentType.NUMBER, @@ -1447,7 +1447,7 @@ disableMonitor: true, opcode: "RGB2HEX", blockType: Scratch.BlockType.REPORTER, - text: "Red [R] Green [G] Blue [B]", + text: "red [R] green [G] blue [B]", arguments: { R: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, G: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, @@ -1458,7 +1458,7 @@ disableMonitor: true, opcode: "HSV2RGB", blockType: Scratch.BlockType.REPORTER, - text: "Hue [H] Saturation [S] Value [V]", + text: "hue [H] saturation [S] value [V]", arguments: { H: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }, S: { type: Scratch.ArgumentType.NUMBER, defaultValue: 100 }, From 3a65c233a79324fe1a940bec908910a19e23977b Mon Sep 17 00:00:00 2001 From: Muffin Date: Sun, 5 Nov 2023 17:59:37 -0600 Subject: [PATCH 14/14] npm run format --- extensions/obviousAlexC/penPlus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/obviousAlexC/penPlus.js b/extensions/obviousAlexC/penPlus.js index 11ee1c5152..3d6c9fd469 100644 --- a/extensions/obviousAlexC/penPlus.js +++ b/extensions/obviousAlexC/penPlus.js @@ -1751,7 +1751,7 @@ menuIconURI: "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMi45OTk3MiIgaGVpZ2h0PSIzMi44ODIwNyIgdmlld0JveD0iMCwwLDMyLjk5OTcyLDMyLjg4MjA3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI0LC0xNjMuOTk5OTMpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIyOC43NTMsMTk0LjYwMmwtNC4yNSwxLjc4bDEuNzgzLC00LjIzN2MxLjIxOCwtMi44OTIgMi45MDcsLTUuNDIzIDUuMDMsLTcuNTM4bDE5Ljc1LC0xOS42NzdjMC44NDYsLTAuODQyIDIuNjUsLTAuNDEgNC4wMzIsMC45NjdjMS4zOCwxLjM3NSAxLjgxNiwzLjE3MyAwLjk3LDQuMDE1bC0xOS43NSwxOS42NzhjLTIuMTIzLDIuMTE2IC00LjY2NCwzLjggLTcuNTY1LDUuMDEyIiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTIzNi44NTgsMTczLjQyOGMwLDAgMi42MTYsMi4yMiA0LjM1LC0xLjU0NmMzLjc1MiwtOC4xNSA4LjIwMiwtNS43NzIgOC4yMDIsLTUuNzcyIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI1Ni40MiwxNjguODI1YzAsMC40NjMgLTAuMTQsMC44NzMgLTAuNDMyLDEuMTY0bC05LjMzNSw5LjNjMC4yODIsLTAuMjkgMC40MSwtMC42NjggMC40MSwtMS4xMmMwLC0wLjg3NCAtMC41MDcsLTEuOTYzIC0xLjQwNiwtMi44NjhjLTEuMzYyLC0xLjM1OCAtMy4xNDcsLTEuOCAtNC4wMDIsLTAuOTlsOS4zMzUsLTkuMzAxYzAuODQ0LC0wLjg0IDIuNjUsLTAuNDEgNC4wMzUsMC45NmMwLjg5OCwwLjkwNCAxLjM5NiwxLjk4MiAxLjM5NiwyLjg1NU0yMzAuNTE1LDE5My43NzRjLTAuNTczLDAuMzAyIC0xLjE1NywwLjU3IC0xLjc2NCwwLjgzbC00LjI1MSwxLjc3OGwxLjc4NiwtNC4yMzVjMC4yNTgsLTAuNjA0IDAuNTMsLTEuMTg2IDAuODMzLC0xLjc1N2MwLjY5LDAuMTgzIDEuNDQ4LDAuNjI1IDIuMTA4LDEuMjgyYzAuNjYsMC42NTggMS4xMDIsMS40MTIgMS4yODcsMi4xMDIiIGZpbGw9IiM0Yzk3ZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjU2LjQ5OCwxNjguNzQ4YzAsMC40NjQgLTAuMTQsMC44NzQgLTAuNDMzLDEuMTY1bC0xOS43NDIsMTkuNjhjLTIuMTMsMi4xMSAtNC42NzMsMy43OTMgLTcuNTcyLDUuMDFsLTQuMjUxLDEuNzc3bDAuOTc0LC0yLjMxNmwxLjkyNSwtMC44MDhjMi44OTgsLTEuMjE4IDUuNDQsLTIuOSA3LjU3LC01LjAxbDE5Ljc0MywtMTkuNjhjMC4yOTIsLTAuMjkyIDAuNDMyLC0wLjcwMiAwLjQzMiwtMS4xNjVjMCwtMC42NDYgLTAuMjcsLTEuNCAtMC43OCwtMi4xMjJjMC4yNSwwLjE3MiAwLjUsMC4zNzcgMC43MzcsMC42MTRjMC44OTgsMC45MDUgMS4zOTYsMS45ODMgMS4zOTYsMi44NTYiIGZpbGw9IiM1NzVlNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIG9wYWNpdHk9IjAuMTUiLz48cGF0aCBkPSJNMjM4LjQ1LDE3Mi44M2MwLDAuNSAtMC40MDQsMC45MDUgLTAuOTA0LDAuOTA1Yy0wLjUsMCAtMC45MDUsLTAuNDA1IC0wLjkwNSwtMC45MDRjMCwtMC41IDAuNDA3LC0wLjkwMyAwLjkwNiwtMC45MDNjMC41LDAgMC45MDQsMC40MDQgMC45MDQsMC45MDR6IiBmaWxsPSIjNTc1ZTc1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTI0NC45OTgwNywxODcuMDUyOThoOS41MTc2NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTI0OS43NTY4OSwxOTEuODExOHYtOS41MTc2NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTY6MTYuMDAwMDY5MjMwODQyMTQzLS0+", blockIconURI: - "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==" + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==", }; } costumeMenuFunction() {