Experimental MS - PenguinMod extension
A downloadable project
An extension for https://penguinmod.com/editor.html
! Warning !
Not proved to work!
To use v2, simply rename the file extension name to a .js instead of a .txt
Code:
/*
This extension was made with TurboBuilder!
https://turbobuilder-steel.vercel.app/
*/
(async function(Scratch) {
const variables = {};
const blocks = [];
const menus = [];
if (!Scratch.extensions.unsandboxed) {
alert("This extension needs to be unsandboxed to run!")
return
}
function doSound(ab, cd, runtime) {
const audioEngine = runtime.audioEngine;
const fetchAsArrayBufferWithTimeout = (url) =>
new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
let timeout = setTimeout(() => {
xhr.abort();
reject(new Error("Timed out"));
}, 5000);
xhr.onload = () => {
clearTimeout(timeout);
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error(`HTTP error ${xhr.status} while fetching ${url}`));
}
};
xhr.onerror = () => {
clearTimeout(timeout);
reject(new Error(`Failed to request ${url}`));
};
xhr.responseType = "arraybuffer";
xhr.open("GET", url);
xhr.send();
});
const soundPlayerCache = new Map();
const decodeSoundPlayer = async (url) => {
const cached = soundPlayerCache.get(url);
if (cached) {
if (cached.sound) {
return cached.sound;
}
throw cached.error;
}
try {
const arrayBuffer = await fetchAsArrayBufferWithTimeout(url);
const soundPlayer = await audioEngine.decodeSoundPlayer({
data: {
buffer: arrayBuffer,
},
});
soundPlayerCache.set(url, {
sound: soundPlayer,
error: null,
});
return soundPlayer;
} catch (e) {
soundPlayerCache.set(url, {
sound: null,
error: e,
});
throw e;
}
};
const playWithAudioEngine = async (url, target) => {
const soundBank = target.sprite.soundBank;
let soundPlayer;
try {
const originalSoundPlayer = await decodeSoundPlayer(url);
soundPlayer = originalSoundPlayer.take();
} catch (e) {
console.warn(
"Could not fetch audio; falling back to primitive approach",
e
);
return false;
}
soundBank.addSoundPlayer(soundPlayer);
await soundBank.playSound(target, soundPlayer.id);
delete soundBank.soundPlayers[soundPlayer.id];
soundBank.playerTargets.delete(soundPlayer.id);
soundBank.soundEffects.delete(soundPlayer.id);
return true;
};
const playWithAudioElement = (url, target) =>
new Promise((resolve, reject) => {
const mediaElement = new Audio(url);
mediaElement.volume = target.volume / 100;
mediaElement.onended = () => {
resolve();
};
mediaElement
.play()
.then(() => {
// Wait for onended
})
.catch((err) => {
reject(err);
});
});
const playSound = async (url, target) => {
try {
if (!(await Scratch.canFetch(url))) {
throw new Error(`Permission to fetch ${url} denied`);
}
const success = await playWithAudioEngine(url, target);
if (!success) {
return await playWithAudioElement(url, target);
}
} catch (e) {
console.warn(`All attempts to play ${url} failed`, e);
}
};
playSound(ab, cd)
}
class Extension {
getInfo() {
return {
"id": "extensionID",
"name": "Experimental MS",
"color1": "#ff0000",
"color2": "#b80000",
"tbShow": true,
"blocks": blocks
}
}
}
blocks.push({
opcode: `is leap year?`,
blockType: Scratch.BlockType.BOOLEAN,
text: `is leap year?`,
arguments: {},
disableMonitor: true
});
Extension.prototype[`is leap year?`] = async (args, util) => {
return ((new Date(new Date(Date.now()).getYear(), 1, 29)).getDate() === 29)
};
return ((new Date(new Date(Date.now()).getYear(), 1, 29)).getDate() === 29)
blocks.push({
opcode: `null`,
blockType: Scratch.BlockType.REPORTER,
text: `null`,
arguments: {},
disableMonitor: true
});
Extension.prototype[`null`] = async (args, util) => {
return null
};
return null
Scratch.extensions.register(new Extension());
})(Scratch);
Status | In development |
Category | Other |
Author | Team Sapphire |