Merge branch 'main' into Alert/Confirm/Prompt

This commit is contained in:
sussy layers dev 2023-11-10 22:13:23 +00:00 committed by GitHub
commit 3dbd97449a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 281 additions and 27 deletions

View file

@ -109,7 +109,7 @@
</head>
<body data-sveltekit-preload-data="hover">
<div class="__alert_prompt" id="Zm5BSEgzQW5HeW9yZkFkNEhpbGJiamtpZmdoOGNDc2lMR29JSzF0K2JnPT0=">
<p style="margin-block: 0;">TurboBuilder is currently in development. Features may not work correctly here.</p>
<p style="margin-block: 0;">TurboBuilder is currently in development, some project files may break due to code changing all the time.</p>
<button
style="margin: 0; margin-left: 8px; background: transparent; border: 0; outline: 0; padding: 0; cursor: pointer;"
onclick="document.getElementById('Zm5BSEgzQW5HeW9yZkFkNEhpbGJiamtpZmdoOGNDc2lMR29JSzF0K2JnPT0=').remove()"

View file

@ -1,16 +1,26 @@
<xml>
<category name="Events" colour="#FFBF00">
<block type="events_interval" />
<block type="events_timeout" />
</category>
<category name="Control" colour="#FFAB19">
<block type="control_ifthen" />
<block type="control_ifthenelse" />
<block type="control_ifthenreturn" />
<sep gap="48"></sep>
<block type="control_switch" />
<block type="control_case" />
<block type="control_default" />
<block type="control_break" />
</category>
<category name="Sound" colour="#CF63CF">
<block type="sound_startsound" />
</category>
<category name="Sensing" colour="#5CB1D6">
<!--
<block type="sensing_keypress" />
<sep gap="48"></sep>
-->
<block type="sensing_alert" />
<block type="sensing_prompt" />
<block type="sensing_confirm" />
@ -19,6 +29,7 @@
<block type="literals_true" />
<block type="literals_false" />
<sep gap="48"></sep>
<block type="literals_null" />
<block type="literals_number" />
<block type="literals_string" />
<block type="literals_color" />

View file

@ -16,7 +16,7 @@ function register() {
{
"type": "field_input",
"name": "ID",
"value": "id",
"text": "id",
"spellcheck": false
},
{
@ -25,7 +25,7 @@ function register() {
{
"type": "field_input",
"name": "TEXT",
"value": "text",
"text": "text",
"spellcheck": false
},
{
@ -75,7 +75,7 @@ function register() {
text: \`${TEXT}\`,
arguments: { ${INPUTS} }
})
Extension.prototype[\`${ID}\`] = (args) => { ${FUNC} }`;
Extension.prototype[\`${ID}\`] = (args, util) => { vm = util; console.log(util); ${FUNC} }`;
return `${code}\n`;
})
@ -89,7 +89,7 @@ function register() {
{
"type": "field_input",
"name": "ID",
"value": "ID",
"text": "ID",
"spellcheck": false
},
{
@ -135,7 +135,7 @@ function register() {
{
"type": "field_input",
"name": "NAME",
"value": "INPUTID",
"text": "INPUTID",
"spellcheck": false
}
],

View file

@ -73,6 +73,34 @@ function register() {
return `${code}\n`;
})
// if <> then () else ()
registerBlock(`${categoryPrefix}ifthenreturn`, {
message0: 'if %1 then %2 else %3',
args0: [
{
"type": "input_value",
"name": "CONDITION",
"check": "Boolean"
},
{
"type": "input_value",
"name": "X",
},
{
"type": "input_value",
"name": "Y",
},
],
output: null,
inputsInline: false,
colour: categoryColor
}, (block) => {
const CONDITION = javascriptGenerator.valueToCode(block, 'CONDITION', javascriptGenerator.ORDER_ATOMIC);
const X = javascriptGenerator.valueToCode(block, 'X', javascriptGenerator.ORDER_ATOMIC);
const Y = javascriptGenerator.valueToCode(block, 'Y', javascriptGenerator.ORDER_ATOMIC);
return [`(${CONDITION || false} ? ${X} : ${Y})`, javascriptGenerator.ORDER_ATOMIC];
})
// switch statement
registerBlock(`${categoryPrefix}switch`, {
message0: 'switch %1 %2 %3',

View file

@ -0,0 +1,62 @@
import javascriptGenerator from '../javascriptGenerator';
import registerBlock from '../register';
const categoryPrefix = 'events_';
const categoryColor = '#FFBF00';
function register() {
// setInterval
registerBlock(`${categoryPrefix}interval`, {
message0: 'every %1 seconds do %2 %3',
args0: [
{
"type": "input_value",
"name": "TIME",
"check": "Number"
},
{
"type": "input_dummy"
},
{
"type": "input_statement",
"name": "BLOCKS"
}
],
inputsInline: true,
colour: categoryColor,
}, (block) => {
const TIME = javascriptGenerator.valueToCode(block, 'TIME', javascriptGenerator.ORDER_ATOMIC);
const BLOCKS = javascriptGenerator.statementToCode(block, 'BLOCKS');
const code = `setInterval(() => { ${BLOCKS} }, (${TIME} * 1000));`;
return `${code}\n`;
})
// setTimeout
registerBlock(`${categoryPrefix}timeout`, {
message0: 'in %1 seconds do %2 %3',
args0: [
{
"type": "input_value",
"name": "TIME",
"check": "Number"
},
{
"type": "input_dummy"
},
{
"type": "input_statement",
"name": "BLOCKS"
}
],
previousStatement: null,
nextStatement: null,
inputsInline: true,
colour: categoryColor,
}, (block) => {
const TIME = javascriptGenerator.valueToCode(block, 'TIME', javascriptGenerator.ORDER_ATOMIC);
const BLOCKS = javascriptGenerator.statementToCode(block, 'BLOCKS');
const code = `setTimeout(() => { ${BLOCKS} }, (${TIME} * 1000));`;
return `${code}\n`;
})
}
export default register;

View file

@ -46,6 +46,17 @@ function register() {
return [NUMBER, javascriptGenerator.ORDER_ATOMIC];
})
// null
registerBlock(`${categoryPrefix}null`, {
message0: 'null',
args0: [],
output: "Null",
inputsInline: true,
colour: categoryColor
}, (block) => {
return ['null', javascriptGenerator.ORDER_ATOMIC];
})
// string
registerBlock(`${categoryPrefix}string`, {
message0: "'%1'",
@ -53,7 +64,7 @@ function register() {
{
"type": "field_input",
"name": "STRING",
"value": 0,
"text": "string",
"spellcheck": false
}
],

View file

@ -0,0 +1,30 @@
import javascriptGenerator from '../javascriptGenerator';
import registerBlock from '../register';
const categoryPrefix = 'sound_';
const categoryColor = '#CF63CF';
function register() {
// start playing a sound (and also it needs to load lol!!)
registerBlock(`${categoryPrefix}startsound`, {
message0: 'start sound %1',
args0: [
{
"type": "field_input",
"name": "SOUND",
"text": "https://t.ly/2gHlM",
"spellcheck": false
},
],
previousStatement: null,
nextStatement: null,
inputsInline: true,
colour: categoryColor,
}, (block) => {
const SOUND = block.getFieldValue('SOUND')
const code = `doSound(\`${SOUND}\`, Scratch.vm.runtime.targets.find(target => target.isStage), Scratch.vm.runtime);`;
return `${code}\n`;
})
}
export default register;

View file

@ -1,13 +1,13 @@
const throwAwayVars = {}; // used for repeat loops
const compileVars = {};
export const compileVars = {};
compileVars._idx = 0;
compileVars.new = () => {
const _listLow = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
const _listHigh = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
const _listSym = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '@', '#', '$', '%', '&', '(', ')', '_', '-', '+', '=', '[', ']', '|'];
const list = [].concat(_listLow, _listHigh, _listSym);
const list = [].concat(_listLow, _listHigh);
let str = '';
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 16; i++) {
str += list[Math.round(Math.random() * (list.length - 1))];
};
return str;

View file

@ -1,5 +1,127 @@
import javascriptGenerator from '../javascriptGenerator';
const start = `
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 Compiler {
/**
* Generates JavaScript code from the provided workspace & info.
@ -19,7 +141,9 @@ class Compiler {
`(function (Scratch) {`,
`const variables = {};`,
`const blocks = [];`,
`const menus = [];`
`const menus = [];`,
``,
start
];
const classRegistry = {
top: [

View file

@ -51,8 +51,10 @@
registerGeneric();
import registerCore from "../resources/blocks/core.js";
import registerEvents from "../resources/blocks/events.js";
import registerControl from "../resources/blocks/control.js";
import registerSensing from "../resources/blocks/sensing.js";
import registerSound from "../resources/blocks/sound.js";
import registerLiterals from "../resources/blocks/literals.js";
import registerOperators from "../resources/blocks/operators.js";
import registerVariables from "../resources/blocks/variables.js";
@ -61,6 +63,8 @@
registerCore();
registerControl();
registerEvents();
registerSound();
registerSensing();
registerLiterals();
registerOperators();
@ -531,22 +535,6 @@
>
Download
</StyledButton>
<div style="margin-right: 8px" />
<StyledButton
on:click={() => {
window.open("https://turbowarp.org/editor?extension=" + encodeURI("data:text/plain;base64," + btoa(lastGeneratedCode)), '_blank').focus();
}}
>
TurboWarp
</StyledButton>
<div style="margin-right: 4px" />
<StyledButton
on:click={() => {
window.open("https://studio.penguinmod.com/editor?extension=" + encodeURI("data:text/plain;base64," + btoa(lastGeneratedCode)), '_blank').focus();
}}
>
PenguinMod
</StyledButton>
</div>
<div class="codeWrapper">
<div class="codeDisplay">