diff --git a/blocks/sound.js b/blocks/sound.js index cabfb2b9..bbf93ec6 100644 --- a/blocks/sound.js +++ b/blocks/sound.js @@ -6,7 +6,12 @@ import { getHelpUrlFor, registerBlockHandler, } from "./blocks.js"; -import { audioNames } from "../config.js"; +import { + audioNames, + themeNames, + audioFileToLabel, + getThemeDisplayName, +} from "../config.js"; import { translate, getTooltip, @@ -14,6 +19,79 @@ import { } from "../main/translation.js"; export function defineSoundBlocks() { + Blockly.Blocks["play_theme"] = { + init: function () { + const variableNamePrefix = "sound"; + let nextVariableName = + variableNamePrefix + nextVariableIndexes[variableNamePrefix]; + this.jsonInit({ + type: "play_theme", + message0: translate("play_theme"), + args0: [ + { + type: "field_variable", + name: "ID_VAR", + variable: nextVariableName, + }, + { + type: "field_dropdown", + name: "THEME_NAME", + options: function () { + return themeNames.map((name) => [getThemeDisplayName(name), name]); + }, + }, + { + type: "input_dummy", + name: "MESH_INPUT", + }, + { + type: "input_value", + name: "SPEED", + value: 1, + min: 0.1, + max: 3, + precision: 0.1, + }, + { + type: "input_value", + name: "VOLUME", + value: 1, + min: 0, + max: 1, + precision: 0.1, + }, + { + type: "field_dropdown", + name: "MODE", + options: [getDropdownOption("ONCE"), getDropdownOption("LOOP")], + }, + { + type: "field_dropdown", + name: "ASYNC", + options: [getDropdownOption("START"), getDropdownOption("AWAIT")], + }, + ], + inputsInline: true, + previousStatement: null, + nextStatement: null, + colour: categoryColours["Sound"], + tooltip: getTooltip("play_theme"), + extensions: ["dynamic_mesh_dropdown"], + }); + this.setHelpUrl(getHelpUrlFor(this.type)); + this.setStyle("sound_blocks"); + + registerBlockHandler(this, (changeEvent) => { + handleBlockCreateEvent( + this, + changeEvent, + variableNamePrefix, + nextVariableIndexes, + ); + }); + }, + }; + Blockly.Blocks["play_sound"] = { init: function () { const variableNamePrefix = "sound"; @@ -32,7 +110,7 @@ export function defineSoundBlocks() { type: "field_dropdown", name: "SOUND_NAME", options: function () { - return audioNames.map((name) => [name, name]); + return audioNames.map((name) => [audioFileToLabel(name), name]); }, }, { diff --git a/config.js b/config.js index e56c8ea4..25d83ec6 100644 --- a/config.js +++ b/config.js @@ -1,12 +1,15 @@ import { getDropdownOption, translate } from "./main/translation.js"; -export const audioNames = [ +export const themeNames = [ "theme-bright.mp3", "theme-calm.mp3", "theme-electronic.mp3", "theme-game.mp3", "theme-medieval.mp3", "theme-metal.mp3", +]; + +export const audioNames = [ "highDown.mp3", "highUp.mp3", "laser1.mp3", @@ -71,6 +74,25 @@ export const audioNames = [ "zapTwoTone.mp3", ]; +export function audioFileToLabel(filename) { + return filename + .replace(".mp3", "") + .replace(/([A-Z])/g, " $1") + .replace(/(\d+)/g, " $1") + .trim() + .replace(/\s+/g, " ") + .replace(/\b\w/g, (c) => c.toUpperCase()); +} + +export function getThemeDisplayName(filename) { + const baseName = filename.replace("theme-", "").replace(".mp3", ""); + const key = "theme_" + baseName + "_option"; + const translated = translate(key); + return translated && translated !== key + ? translated + : baseName.charAt(0).toUpperCase() + baseName.slice(1); +} + export const characterNames = [ "Liz1.glb", "Liz2.glb", diff --git a/examples/my_place.flock b/examples/my_place.flock index efcc0bb0..04e2467d 100644 --- a/examples/my_place.flock +++ b/examples/my_place.flock @@ -992,13 +992,13 @@ "inputs": { "DO": { "block": { - "type": "play_sound", + "type": "play_theme", "id": "8#aLU7$bN@?eb8roJxH%", "fields": { "ID_VAR": { "id": "sT@2f`QQg?Wd*tit,y/6" }, - "SOUND_NAME": "theme-electronic.mp3", + "THEME_NAME": "theme-electronic.mp3", "MESH_NAME": "musicbox", "MODE": "ONCE", "ASYNC": "START" diff --git a/generators/generators.js b/generators/generators.js index 8e8877a8..720ace05 100644 --- a/generators/generators.js +++ b/generators/generators.js @@ -1983,6 +1983,39 @@ export function defineGenerators() { return [code, javascriptGenerator.ORDER_NONE]; }; + javascriptGenerator.forBlock["play_theme"] = function (block) { + const idVar = javascriptGenerator.nameDB_.getName( + block.getFieldValue("ID_VAR"), + Blockly.Names.NameType.VARIABLE, + ); + + const meshNameField = block.getFieldValue("MESH_NAME"); + const meshName = `"${meshNameField}"`; + + const themeName = block.getFieldValue("THEME_NAME"); + + const speedCode = + javascriptGenerator.valueToCode( + block, + "SPEED", + javascriptGenerator.ORDER_ATOMIC, + ) || "1"; + + const volumeCode = + javascriptGenerator.valueToCode( + block, + "VOLUME", + javascriptGenerator.ORDER_ATOMIC, + ) || "1"; + + const loop = block.getFieldValue("MODE") === "LOOP"; + const asyncMode = block.getFieldValue("ASYNC"); + + const code = `${idVar} = ${asyncMode === "AWAIT" ? "await " : ""}playSound(${meshName}, { soundName: "${themeName}", loop: ${loop}, volume: ${volumeCode}, playbackRate: ${speedCode} });\n`; + + return code; + }; + javascriptGenerator.forBlock["play_sound"] = function (block) { const idVar = javascriptGenerator.nameDB_.getName( block.getFieldValue("ID_VAR"), diff --git a/locale/de.js b/locale/de.js index b1a5a6f4..d1e33219 100644 --- a/locale/de.js +++ b/locale/de.js @@ -284,6 +284,8 @@ export default { "Ebene hinzufügen %1 %2 Breite: %3 Höhe: %4\nbei x: %5 y: %6 z: %7", // Sound blocks + play_theme: + "Thema abspielen %1 %2 von %3\nGeschwindigkeit: %4 Lautstärke: %5 Modus: %6 Async: %7", play_sound: "Ton abspielen %1 %2 von %3\nGeschwindigkeit: %4 Lautstärke: %5 Modus: %6 Async: %7", stop_all_sounds: "Alle Töne stoppen", @@ -716,6 +718,8 @@ export default { "Erstelle eine farbige 2D-Fläche mit Breite, Höhe und Position.\nSchlüsselwort: plane", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Spiele ein Musikthema auf einem Objekt mit anpassbarer Geschwindigkeit, Lautstärke und Modus.\nSchlüsselwort: theme", play_sound_tooltip: "Spiele den ausgewählten Sound auf einem Objekt mit anpassbarer Geschwindigkeit, Lautstärke und Modus.\nSchlüsselwort: sound", stop_all_sounds_tooltip: diff --git a/locale/en.js b/locale/en.js index d99ec65f..dc8fffc6 100644 --- a/locale/en.js +++ b/locale/en.js @@ -280,6 +280,8 @@ export default { create_plane: "add plane %1 %2 width: %3 height: %4 \nat x: %5 y: %6 z: %7", // Custom block translations - Sound blocks + play_theme: + "play theme %1 %2 from %3 \nspeed: %4 volume: %5 mode: %6 async: %7", play_sound: "play sound %1 %2 from %3 \nspeed: %4 volume: %5 mode: %6 async: %7", stop_all_sounds: "stop all sounds", @@ -565,6 +567,8 @@ export default { "Create a colored 2D plane with specified width, height, and position.\nKeyword: plane", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Play a theme tune on a mesh with adjustable speed, volume, and mode.\nKeyword: theme", play_sound_tooltip: "Play the selected sound on a mesh with adjustable speed, volume, and mode.\nKeyword: sound", stop_all_sounds_tooltip: @@ -888,6 +892,13 @@ export default { ONCE_option: "once", LOOP_option: "loop", + theme_bright_option: "Bright", + theme_calm_option: "Calm", + theme_electronic_option: "Electronic", + theme_game_option: "Game", + theme_medieval_option: "Medieval", + theme_metal_option: "Metal", + sine_option: "sine", square_option: "square", sawtooth_option: "sawtooth", diff --git a/locale/es.js b/locale/es.js index 250da9e2..cf75bfc8 100644 --- a/locale/es.js +++ b/locale/es.js @@ -277,6 +277,8 @@ export default { create_plane: "añadir plano %1 %2 ancho: %3 alto: %4 \nen x: %5 y: %6 z: %7", // human // Custom block translations - Sound blocks + play_theme: + "reproducir tema %1 %2 desde %3 \nvelocidad: %4 volumen: %5 modo: %6 asíncrono: %7", // ai play_sound: "reproducir sonido %1 %2 desde %3 \nvelocidad: %4 volumen: %5 modo: %6 asíncrono: %7", // human stop_all_sounds: "parar todos los sonidos", // human @@ -579,6 +581,8 @@ export default { "Crea un plano 2D de color con ancho, alto y posición especificadas.\nPalabra clave: plano", // human // Tooltip translations - Sound blocks + play_theme_tooltip: + "Reproduce un tema musical en una malla con velocidad, volumen y modo ajustables.\nPalabra clave: tema", // ai play_sound_tooltip: "Reproduce el sonido seleccionado en una malla con velocidad, volumen y modo ajustables.\nPalabra clave: sonido", // human stop_all_sounds_tooltip: @@ -903,6 +907,13 @@ export default { ONCE_option: "una vez", // human LOOP_option: "bucle", // human + theme_bright_option: "Brillante", // ai + theme_calm_option: "Tranquilo", // ai + theme_electronic_option: "Electrónico", // ai + theme_game_option: "Videojuego", // ai + theme_medieval_option: "Medieval", // ai + theme_metal_option: "Metal", // ai + sine_option: "seno", // human square_option: "cuadrada", // human sawtooth_option: "diente de sierra", // human diff --git a/locale/fr.js b/locale/fr.js index 7549337f..cdc8722a 100644 --- a/locale/fr.js +++ b/locale/fr.js @@ -279,6 +279,8 @@ export default { "ajouter plan %1 %2 largeur: %3 hauteur: %4\nà x: %5 y: %6 z: %7", // Custom block translations - Sound blocks + play_theme: + "jouer le thème %1 %2 depuis %3\nvitesse: %4 volume: %5 mode: %6 asynchrone: %7", play_sound: "jouer le son %1 %2 depuis %3\nvitesse: %4 volume: %5 mode: %6 asynchrone: %7", stop_all_sounds: "arrêter tous les sons", @@ -579,6 +581,8 @@ export default { "Crée un plan 2D coloré avec largeur, hauteur et position spécifiées.\nMot-clé: plane", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Joue un thème musical sur un maillage avec vitesse, volume et mode réglables.\nMot-clé: theme", play_sound_tooltip: "Joue le son sélectionné sur un maillage avec vitesse, volume et mode réglables.\nMot-clé: sound", stop_all_sounds_tooltip: diff --git a/locale/it.js b/locale/it.js index f119be2d..32088e24 100644 --- a/locale/it.js +++ b/locale/it.js @@ -283,6 +283,8 @@ export default { "aggiungi piano %1 %2 larghezza: %3 altezza: %4 \na x: %5 y: %6 z: %7", // Custom block translations - Sound blocks + play_theme: + "riproduci tema %1 %2 da %3 \nvelocità: %4 volume: %5 modalità: %6 asincrono: %7", play_sound: "riproduci suono %1 %2 da %3 \nvelocità: %4 volume: %5 modalità: %6 asincrono: %7", stop_all_sounds: "ferma tutti i suoni", @@ -578,6 +580,8 @@ export default { "Crea un piano 2D colorato con larghezza, altezza e posizione specificate.\nParola chiave: plane", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Riproduce un tema musicale su una mesh con velocità, volume e modalità regolabili.\nParola chiave: theme", play_sound_tooltip: "Riproduce il suono selezionato su una mesh con velocità, volume e modalità regolabili.\nParola chiave: sound", stop_all_sounds_tooltip: diff --git a/locale/pl.js b/locale/pl.js index c6abf040..9ad7b423 100644 --- a/locale/pl.js +++ b/locale/pl.js @@ -280,6 +280,8 @@ export default { "dodaj płaszczyznę %1 %2 szerokość: %3 wysokość: %4\nw x: %5 y: %6 z: %7", // Custom block translations - Sound blocks + play_theme: + "odtwórz motyw %1 %2 od %3\nprędkość: %4 głośność: %5 tryb: %6 async: %7", play_sound: "odtwórz dźwięk %1 %2 od %3\nprędkość: %4 głośność: %5 tryb: %6 async: %7", stop_all_sounds: "zatrzymaj wszystkie dźwięki", @@ -573,6 +575,8 @@ export default { "Stwórz kolorową płaszczyznę 2D %1 %2 szer. %3 wys. %4\npołożenie x: %5 y: %6 z: %7\nSłowo kluczowe: plane", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Odtwórz motyw muzyczny na siatce z regulowaną prędkością, głośnością i trybem.\nSłowo kluczowe: theme", play_sound_tooltip: "Odtwórz dźwięk %1 %2 na siatce od %3\nprędkość: %4, głośność: %5, tryb: %6, async: %7\nSłowo kluczowe: sound", stop_all_sounds_tooltip: diff --git a/locale/pt.js b/locale/pt.js index d5b39df7..7de0c354 100644 --- a/locale/pt.js +++ b/locale/pt.js @@ -278,6 +278,8 @@ export default { "adicionar plano %1 %2 largura: %3 altura: %4 \nem x: %5 y: %6 z: %7", // Custom block translations - Sound blocks + play_theme: + "tocar tema %1 %2 de %3 \nvelocidade: %4 volume: %5 modo: %6 assíncrono: %7", play_sound: "tocar som %1 %2 de %3 \nvelocidade: %4 volume: %5 modo: %6 assíncrono: %7", stop_all_sounds: "parar todos os sons", @@ -569,6 +571,8 @@ export default { "Cria um plano 2D colorido com largura, altura e posição especificadas.\nPalavra-chave: plano", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Reproduz um tema musical em um mesh com velocidade, volume e modo ajustáveis.\nPalavra-chave: tema", play_sound_tooltip: "Reproduz o som selecionado em um mesh com velocidade, volume e modo ajustáveis.\nPalavra-chave: som", stop_all_sounds_tooltip: diff --git a/locale/sv.js b/locale/sv.js index 3608e0da..40d532a8 100644 --- a/locale/sv.js +++ b/locale/sv.js @@ -278,6 +278,8 @@ export default { "lägg till plan %1 %2 bredd: %3 höjd: %4 \nvid x: %5 y: %6 z: %7", // Custom block translations - Sound blocks + play_theme: + "spela tema %1 %2 från %3 \nhastighet: %4 volym: %5 läge: %6 asynkront: %7", play_sound: "spela ljud %1 %2 från %3 \nrotation: %4 volym: %5 läge: %6 asynkront: %7", stop_all_sounds: "stoppa alla ljud", @@ -564,6 +566,8 @@ export default { "Skapa ett färgat 2D-plan med angiven bredd, höjd och position.\nKeyword: plane", // Tooltip translations - Sound blocks + play_theme_tooltip: + "Spela upp ett musiktema på ett mesh med justerbar hastighet, volym och läge.\nKeyword: theme", play_sound_tooltip: "Spela upp det valda ljudet på ett mesh med justerbar hastighet, volym och läge.\nKeyword: sound", stop_all_sounds_tooltip: diff --git a/toolbox.js b/toolbox.js index 39ffbe65..eac94f77 100644 --- a/toolbox.js +++ b/toolbox.js @@ -3231,6 +3231,29 @@ const toolboxSound = { //colour: categoryColours["Sound"], categorystyle: "sound_category", contents: [ + { + kind: "block", + type: "play_theme", + keyword: "theme", + inputs: { + SPEED: { + shadow: { + type: "math_number", + fields: { + NUM: 1, + }, + }, + }, + VOLUME: { + shadow: { + type: "math_number", + fields: { + NUM: 1, + }, + }, + }, + }, + }, { kind: "block", type: "play_sound",