From f3be057200f47e1cb35d532d2018ef18fefdbdef Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:43:56 +0100 Subject: [PATCH 1/8] Drop LIBSCRATCHCPP_COMPUTED_GOTO option --- CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bb3d052..416b4595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,24 +8,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON) option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON) -option(LIBSCRATCHCPP_COMPUTED_GOTO "Support for computed goto" ON) option(LIBSCRATCHCPP_USE_LLVM "Compile scripts to LLVM IR (work in progress)" OFF) option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF) -if (NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) - # Computed goto not supported on anything except GCC - set(LIBSCRATCHCPP_COMPUTED_GOTO OFF CACHE BOOL "" FORCE) -endif() - add_library(scratchcpp SHARED) add_subdirectory(src) include_directories(src) # TODO: Remove this line include_directories(include) -if (LIBSCRATCHCPP_COMPUTED_GOTO) - target_compile_definitions(scratchcpp PRIVATE ENABLE_COMPUTED_GOTO) -endif() - target_sources(scratchcpp PUBLIC include/scratchcpp/global.h From 93b23a16bc5e704d7432e65e5b2ba257e061c072 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:49:15 +0100 Subject: [PATCH 2/8] Drop LIBSCRATCHCPP_USE_LLVM option --- CMakeLists.txt | 45 ++++++++++++++------------------------- src/CMakeLists.txt | 8 ++----- src/engine/CMakeLists.txt | 9 -------- test/CMakeLists.txt | 9 +++----- 4 files changed, 21 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 416b4595..f5972c4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON) option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON) -option(LIBSCRATCHCPP_USE_LLVM "Compile scripts to LLVM IR (work in progress)" OFF) option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF) add_library(scratchcpp SHARED) @@ -57,31 +56,21 @@ target_sources(scratchcpp include/scratchcpp/comment.h include/scratchcpp/monitor.h include/scratchcpp/imonitorhandler.h + include/scratchcpp/dev/compiler.h + include/scratchcpp/dev/compilercontext.h + include/scratchcpp/dev/compilervalue.h + include/scratchcpp/dev/compilerconstant.h + include/scratchcpp/dev/compilerlocalvariable.h + include/scratchcpp/dev/executablecode.h + include/scratchcpp/dev/executioncontext.h + include/scratchcpp/dev/promise.h + include/scratchcpp/dev/test/scriptbuilder.h ) -if (LIBSCRATCHCPP_USE_LLVM) - target_compile_definitions(scratchcpp PUBLIC USE_LLVM) - target_sources(scratchcpp - PUBLIC - include/scratchcpp/dev/compiler.h - include/scratchcpp/dev/compilercontext.h - include/scratchcpp/dev/compilervalue.h - include/scratchcpp/dev/compilerconstant.h - include/scratchcpp/dev/compilerlocalvariable.h - include/scratchcpp/dev/executablecode.h - include/scratchcpp/dev/executioncontext.h - include/scratchcpp/dev/promise.h - include/scratchcpp/dev/test/scriptbuilder.h - ) - - if(LIBSCRATCHCPP_PRINT_LLVM_IR) - target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR) - endif() -else() - target_sources(scratchcpp - PUBLIC - include/scratchcpp/compiler.h - ) +target_compile_definitions(scratchcpp PUBLIC USE_LLVM) + +if(LIBSCRATCHCPP_PRINT_LLVM_IR) + target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR) endif() include(FetchContent) @@ -113,11 +102,9 @@ if (LIBSCRATCHCPP_NETWORK_SUPPORT) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_NETWORK_SUPPORT) endif() -if (LIBSCRATCHCPP_USE_LLVM) - include(build/HunterPackages.cmake) - include(build/LLVM.cmake) - target_link_libraries(scratchcpp PRIVATE LLVM) -endif() +include(build/HunterPackages.cmake) +include(build/LLVM.cmake) +target_link_libraries(scratchcpp PRIVATE LLVM) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f4e1fcc2..fd084706 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,15 +11,11 @@ target_sources(scratchcpp rect_p.h ) -if (NOT LIBSCRATCHCPP_USE_LLVM) - add_subdirectory(blocks) -endif() +#add_subdirectory(blocks) add_subdirectory(engine) add_subdirectory(internal) add_subdirectory(scratch) add_subdirectory(audio) -if (LIBSCRATCHCPP_USE_LLVM) - add_subdirectory(dev) -endif() +add_subdirectory(dev) diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index d1b47737..d34513d7 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -21,12 +21,3 @@ target_sources(scratchcpp internal/randomgenerator.h internal/randomgenerator.cpp ) - -if(NOT LIBSCRATCHCPP_USE_LLVM) - target_sources(scratchcpp - PRIVATE - compiler.cpp - compiler_p.cpp - compiler_p.h - ) -endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8156363b..80c2a537 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,9 +34,6 @@ add_subdirectory(rect) add_subdirectory(network) add_subdirectory(audio) -if(LIBSCRATCHCPP_USE_LLVM) - add_subdirectory(dev) -else() - add_subdirectory(compiler) - add_subdirectory(blocks) -endif() +add_subdirectory(dev) +#add_subdirectory(compiler) +#add_subdirectory(blocks) From 83813ab0af8c68596ee4d03861aab1cc272dc0b8 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:43:26 +0100 Subject: [PATCH 3/8] Remove computed goto option from minimal unit test workflow --- .github/workflows/utests-minimal.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/utests-minimal.yml b/.github/workflows/utests-minimal.yml index 00e02639..41573123 100644 --- a/.github/workflows/utests-minimal.yml +++ b/.github/workflows/utests-minimal.yml @@ -19,7 +19,7 @@ jobs: submodules: true - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_AUDIO_SUPPORT=OFF -DLIBSCRATCHCPP_NETWORK_SUPPORT=OFF -DLIBSCRATCHCPP_COMPUTED_GOTO=OFF + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_AUDIO_SUPPORT=OFF -DLIBSCRATCHCPP_NETWORK_SUPPORT=OFF - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all) From fe3ff4b3fb3ccaab136ca71ff457c31d9c74b00d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:46:53 +0100 Subject: [PATCH 4/8] Remove LLVM unit test workflow --- .github/workflows/utests-llvm.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/utests-llvm.yml diff --git a/.github/workflows/utests-llvm.yml b/.github/workflows/utests-llvm.yml deleted file mode 100644 index d7d1ace8..00000000 --- a/.github/workflows/utests-llvm.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Unit tests (LLVM compiler) - -on: - push: - branches: '*' - pull_request: - branches: [ "master" ] - -env: - BUILD_TYPE: Debug - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_USE_LLVM=ON - - - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all) - - - name: Run unit tests - run: ctest --test-dir build -V From cfa31e5d0888fff5e2ce35aa9ec687670877c782 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:28:01 +0100 Subject: [PATCH 5/8] Remove old compiler and VM code --- CMakeLists.txt | 1 - include/scratchcpp/block.h | 6 - include/scratchcpp/compiler.h | 92 - include/scratchcpp/global.h | 28 - include/scratchcpp/iengine.h | 7 - include/scratchcpp/imonitorhandler.h | 3 +- include/scratchcpp/inputvalue.h | 6 - include/scratchcpp/monitor.h | 3 +- include/scratchcpp/script.h | 19 - include/scratchcpp/thread.h | 9 - include/scratchcpp/virtualmachine.h | 149 - src/blocks/CMakeLists.txt | 115 - src/blocks/blocks.cpp | 85 - src/blocks/blocks.h | 26 - src/blocks/controlblocks.cpp | 302 -- src/blocks/controlblocks.h | 78 - src/blocks/customblocks.cpp | 81 - src/blocks/customblocks.h | 35 - src/blocks/eventblocks.cpp | 223 -- src/blocks/eventblocks.h | 68 - src/blocks/listblocks.cpp | 219 -- src/blocks/listblocks.h | 60 - src/blocks/looksblocks.cpp | 1102 ------- src/blocks/looksblocks.h | 176 - src/blocks/motionblocks.cpp | 817 ----- src/blocks/motionblocks.h | 125 - src/blocks/operatorblocks.cpp | 306 -- src/blocks/operatorblocks.h | 86 - src/blocks/sensingblocks.cpp | 1095 ------- src/blocks/sensingblocks.h | 184 -- src/blocks/soundblocks.cpp | 426 --- src/blocks/soundblocks.h | 84 - src/blocks/variableblocks.cpp | 178 -- src/blocks/variableblocks.h | 50 - src/engine/CMakeLists.txt | 3 - src/engine/compiler.cpp | 385 --- src/engine/compiler_p.cpp | 66 - src/engine/compiler_p.h | 46 - src/engine/internal/engine.cpp | 125 +- src/engine/internal/engine.h | 8 - src/engine/script.cpp | 145 +- src/engine/script_p.h | 17 - src/engine/thread.cpp | 43 - src/engine/thread_p.h | 10 +- src/engine/virtualmachine.cpp | 255 -- src/engine/virtualmachine_p.cpp | 861 ----- src/engine/virtualmachine_p.h | 79 - src/scratch/block.cpp | 11 - src/scratch/inputvalue.cpp | 34 - src/scratch/monitor.cpp | 10 +- src/scratch/monitor_p.h | 2 - test/CMakeLists.txt | 1 - test/blocks/CMakeLists.txt | 179 -- test/blocks/control_blocks_test.cpp | 1016 ------ test/blocks/custom_blocks_test.cpp | 167 - test/blocks/event_blocks_test.cpp | 672 ---- test/blocks/list_blocks_test.cpp | 693 ---- test/blocks/looks_blocks_test.cpp | 2822 ----------------- test/blocks/motion_blocks_test.cpp | 1408 -------- test/blocks/operator_blocks_test.cpp | 1018 ------ test/blocks/sensing_blocks_test.cpp | 2295 -------------- test/blocks/sound_blocks_test.cpp | 882 ------ test/blocks/variable_blocks_test.cpp | 466 --- test/compiler/CMakeLists.txt | 15 - test/compiler/compiler_test.cpp | 880 ----- test/compiler/testextension.cpp | 49 - test/compiler/testextension.h | 35 - test/engine/engine_test.cpp | 79 +- test/load_project/load_project_test.cpp | 2 - test/mocks/enginemock.h | 3 - test/mocks/monitorhandlermock.h | 3 +- test/scratch_classes/block_test.cpp | 16 - test/scratch_classes/inputvalue_test.cpp | 65 - test/scratch_classes/monitor_test.cpp | 19 +- test/script/script_test.cpp | 178 +- test/thread/thread_test.cpp | 11 - test/virtual_machine/CMakeLists.txt | 14 - test/virtual_machine/virtual_machine_test.cpp | 1887 ----------- 78 files changed, 51 insertions(+), 23168 deletions(-) delete mode 100644 include/scratchcpp/compiler.h delete mode 100644 include/scratchcpp/virtualmachine.h delete mode 100644 src/blocks/CMakeLists.txt delete mode 100644 src/blocks/blocks.cpp delete mode 100644 src/blocks/blocks.h delete mode 100644 src/blocks/controlblocks.cpp delete mode 100644 src/blocks/controlblocks.h delete mode 100644 src/blocks/customblocks.cpp delete mode 100644 src/blocks/customblocks.h delete mode 100644 src/blocks/eventblocks.cpp delete mode 100644 src/blocks/eventblocks.h delete mode 100644 src/blocks/listblocks.cpp delete mode 100644 src/blocks/listblocks.h delete mode 100644 src/blocks/looksblocks.cpp delete mode 100644 src/blocks/looksblocks.h delete mode 100644 src/blocks/motionblocks.cpp delete mode 100644 src/blocks/motionblocks.h delete mode 100644 src/blocks/operatorblocks.cpp delete mode 100644 src/blocks/operatorblocks.h delete mode 100644 src/blocks/sensingblocks.cpp delete mode 100644 src/blocks/sensingblocks.h delete mode 100644 src/blocks/soundblocks.cpp delete mode 100644 src/blocks/soundblocks.h delete mode 100644 src/blocks/variableblocks.cpp delete mode 100644 src/blocks/variableblocks.h delete mode 100644 src/engine/compiler.cpp delete mode 100644 src/engine/compiler_p.cpp delete mode 100644 src/engine/compiler_p.h delete mode 100644 src/engine/virtualmachine.cpp delete mode 100644 src/engine/virtualmachine_p.cpp delete mode 100644 src/engine/virtualmachine_p.h delete mode 100644 test/blocks/CMakeLists.txt delete mode 100644 test/blocks/control_blocks_test.cpp delete mode 100644 test/blocks/custom_blocks_test.cpp delete mode 100644 test/blocks/event_blocks_test.cpp delete mode 100644 test/blocks/list_blocks_test.cpp delete mode 100644 test/blocks/looks_blocks_test.cpp delete mode 100644 test/blocks/motion_blocks_test.cpp delete mode 100644 test/blocks/operator_blocks_test.cpp delete mode 100644 test/blocks/sensing_blocks_test.cpp delete mode 100644 test/blocks/sound_blocks_test.cpp delete mode 100644 test/blocks/variable_blocks_test.cpp delete mode 100644 test/compiler/CMakeLists.txt delete mode 100644 test/compiler/compiler_test.cpp delete mode 100644 test/compiler/testextension.cpp delete mode 100644 test/compiler/testextension.h delete mode 100644 test/virtual_machine/CMakeLists.txt delete mode 100644 test/virtual_machine/virtual_machine_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f5972c4b..ccf1329a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,6 @@ target_sources(scratchcpp include/scratchcpp/field.h include/scratchcpp/script.h include/scratchcpp/broadcast.h - include/scratchcpp/virtualmachine.h include/scratchcpp/blockprototype.h include/scratchcpp/block.h include/scratchcpp/istagehandler.h diff --git a/include/scratchcpp/block.h b/include/scratchcpp/block.h index 76c70dc1..fcdf936f 100644 --- a/include/scratchcpp/block.h +++ b/include/scratchcpp/block.h @@ -10,9 +10,7 @@ namespace libscratchcpp class IEngine; class Target; -#ifdef USE_LLVM class CompilerValue; -#endif class Input; class Field; class Comment; @@ -28,11 +26,7 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity Block(const std::string &id, const std::string &opcode); Block(const Block &) = delete; -#ifdef USE_LLVM CompilerValue *compile(Compiler *compiler); -#else - void compile(Compiler *compiler); -#endif const std::string &opcode() const; diff --git a/include/scratchcpp/compiler.h b/include/scratchcpp/compiler.h deleted file mode 100644 index 05ab0c7c..00000000 --- a/include/scratchcpp/compiler.h +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include - -#include "global.h" -#include "spimpl.h" -#include "virtualmachine.h" -#include "value.h" - -namespace libscratchcpp -{ - -class IEngine; -class Block; -class Input; -class InputValue; -class Field; -class Variable; -class List; -class BlockPrototype; -class Entity; -class CompilerPrivate; - -/*! \brief The Compiler class provides an API for compiling scripts of targets to bytecode. */ -class LIBSCRATCHCPP_EXPORT Compiler -{ - public: - enum class SubstackType - { - Loop, - IfStatement - }; - - Compiler(IEngine *engine, Target *target = nullptr); - Compiler(const Compiler &) = delete; - - void init(); - void compile(std::shared_ptr topLevelBlock); - void end(); - - const std::vector &bytecode() const; - const std::vector &hatPredicateBytecode() const; - - IEngine *engine() const; - Target *target() const; - - const std::vector &constInputValues() const; - std::vector constValues() const; - - const std::vector &variables() const; - std::vector variablePtrs() const; - - const std::vector &lists() const; - - void addInstruction(vm::Opcode opcode, const std::initializer_list &args = {}); - void addInput(Input *input); - void addInput(int id); - void addConstValue(const Value &value); - void addFunctionCall(BlockFunc f); - void addProcedureArg(const std::string &procCode, const std::string &argName); - void moveToSubstack(std::shared_ptr substack1, std::shared_ptr substack2, SubstackType type); - void moveToSubstack(std::shared_ptr substack, SubstackType type); - void warp(); - - Input *input(int id) const; - Field *field(int id) const; - std::shared_ptr inputBlock(int id) const; - unsigned int variableIndex(std::shared_ptr varEntity); - unsigned int listIndex(std::shared_ptr listEntity); - unsigned int constIndex(InputValue *value); - unsigned int procedureIndex(const std::string &proc); - long procedureArgIndex(const std::string &procCode, const std::string &argName); - - const std::vector &procedures() const; - - const std::shared_ptr &block() const; - void setBlock(std::shared_ptr block); - - BlockPrototype *procedurePrototype() const; - void setProcedurePrototype(BlockPrototype *prototype); - - const std::unordered_set &unsupportedBlocks() const; - - private: - spimpl::unique_impl_ptr impl; -}; - -} // namespace libscratchcpp diff --git a/include/scratchcpp/global.h b/include/scratchcpp/global.h index e5824008..d1b6de97 100644 --- a/include/scratchcpp/global.h +++ b/include/scratchcpp/global.h @@ -23,36 +23,17 @@ namespace libscratchcpp { -class VirtualMachine; class Compiler; -#ifdef USE_LLVM class CompilerValue; -#endif class Block; class Value; -/*! - * \typedef BlockFunc - * - * BlockFunc is a function pointer for block implementation functions. - */ -using BlockFunc = unsigned int (*)(VirtualMachine *vm); - -#ifdef USE_LLVM /*! * \typedef BlockComp * * BlockComp is a function pointer for functions which are used to compile blocks. */ using BlockComp = CompilerValue *(*)(Compiler *); -#else -/*! - * \typedef BlockComp - * - * BlockComp is a function pointer for functions which are used to compile blocks to bytecode. - */ -using BlockComp = void (*)(Compiler *); -#endif // USE_LLVM /*! * \typedef MonitorNameFunc @@ -68,21 +49,12 @@ using MonitorNameFunc = const std::string &(*)(Block *); */ using MonitorChangeFunc = void (*)(Block *, const Value &newValue); -#ifdef USE_LLVM /*! * \typedef HatPredicateCompileFunc * * HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates. */ using HatPredicateCompileFunc = CompilerValue *(*)(Compiler *vm); -#else -/*! - * \typedef HatPredicateCompileFunc - * - * HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates to bytecode. - */ -using HatPredicateCompileFunc = void (*)(Compiler *vm); -#endif // USE_LLVM } // namespace libscratchcpp diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index a8cffbc2..196ed443 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -6,7 +6,6 @@ #include #include #include -#include #include "global.h" #include "signal.h" @@ -219,12 +218,6 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Returns the timer of the project. */ virtual ITimer *timer() const = 0; - /*! Returns the index of the given block function. */ - virtual unsigned int functionIndex(BlockFunc f) = 0; - - /*! Returns the list of block functions. */ - virtual const std::vector &blockFunctions() const = 0; - /*! * Call this from IExtension#registerBlocks() to add a compile function to a block section. * \see Extensions diff --git a/include/scratchcpp/imonitorhandler.h b/include/scratchcpp/imonitorhandler.h index d31582ba..3b72d5f9 100644 --- a/include/scratchcpp/imonitorhandler.h +++ b/include/scratchcpp/imonitorhandler.h @@ -17,7 +17,8 @@ class LIBSCRATCHCPP_EXPORT IMonitorHandler virtual void init(Monitor *monitor) = 0; - virtual void onValueChanged(const VirtualMachine *vm) = 0; + // TODO: Add onValueChanged() + // virtual void onValueChanged(const VirtualMachine *vm) = 0; virtual void onXChanged(int x) = 0; virtual void onYChanged(int y) = 0; virtual void onVisibleChanged(bool visible) = 0; diff --git a/include/scratchcpp/inputvalue.h b/include/scratchcpp/inputvalue.h index f1580c75..d973e702 100644 --- a/include/scratchcpp/inputvalue.h +++ b/include/scratchcpp/inputvalue.h @@ -10,9 +10,7 @@ namespace libscratchcpp { -#ifdef USE_LLVM class CompilerValue; -#endif class Block; class Entity; class InputValuePrivate; @@ -37,11 +35,7 @@ class LIBSCRATCHCPP_EXPORT InputValue InputValue(Type type = Type::Number); -#ifdef USE_LLVM CompilerValue *compile(Compiler *compiler); -#else - void compile(Compiler *compiler); -#endif Type type() const; void setType(Type newType); diff --git a/include/scratchcpp/monitor.h b/include/scratchcpp/monitor.h index 85f5c8e4..b85decb7 100644 --- a/include/scratchcpp/monitor.h +++ b/include/scratchcpp/monitor.h @@ -56,7 +56,8 @@ class LIBSCRATCHCPP_EXPORT Monitor : public Entity const std::string &opcode() const; - void updateValue(const VirtualMachine *vm); + // TODO: Add updateValue() + // void updateValue(const VirtualMachine *vm); void setValueChangeFunction(MonitorChangeFunc f); void changeValue(const Value &newValue); diff --git a/include/scratchcpp/script.h b/include/scratchcpp/script.h index 895aaaf7..3032cd4c 100644 --- a/include/scratchcpp/script.h +++ b/include/scratchcpp/script.h @@ -14,13 +14,8 @@ namespace libscratchcpp class Target; class Block; class IEngine; -#ifdef USE_LLVM class ExecutableCode; -#endif -class Value; class Thread; -class Variable; -class List; class ScriptPrivate; /*! \brief The Script class represents a compiled Scratch script. */ @@ -33,29 +28,15 @@ class LIBSCRATCHCPP_EXPORT Script Target *target() const; std::shared_ptr topBlock() const; - unsigned int *bytecode() const; - const std::vector &bytecodeVector() const; - void setBytecode(const std::vector &code); - -#ifdef USE_LLVM ExecutableCode *code() const; void setCode(std::shared_ptr code); -#endif - void setHatPredicateBytecode(const std::vector &code); bool runHatPredicate(Target *target); - void setProcedures(const std::vector &procedures); - void setConstValues(const std::vector &values); - void setVariables(const std::vector &variables); - void setLists(const std::vector &lists); - std::shared_ptr start(); std::shared_ptr start(Target *target); private: - BlockFunc *getFunctions() const; - spimpl::unique_impl_ptr impl; }; diff --git a/include/scratchcpp/thread.h b/include/scratchcpp/thread.h index b5936db7..8708b47c 100644 --- a/include/scratchcpp/thread.h +++ b/include/scratchcpp/thread.h @@ -8,11 +8,8 @@ namespace libscratchcpp { -class VirtualMachine; class Target; -#ifdef USE_LLVM class Promise; -#endif class IEngine; class Script; class ThreadPrivate; @@ -24,7 +21,6 @@ class LIBSCRATCHCPP_EXPORT Thread Thread(Target *target, IEngine *engine, Script *script); Thread(const Thread &) = delete; - VirtualMachine *vm() const; Target *target() const; IEngine *engine() const; Script *script() const; @@ -35,13 +31,8 @@ class LIBSCRATCHCPP_EXPORT Thread bool isFinished() const; -#ifdef USE_LLVM std::shared_ptr promise() const; void setPromise(std::shared_ptr promise); -#else - void promise(); - void resolvePromise(); -#endif private: spimpl::unique_impl_ptr impl; diff --git a/include/scratchcpp/virtualmachine.h b/include/scratchcpp/virtualmachine.h deleted file mode 100644 index e72aef4f..00000000 --- a/include/scratchcpp/virtualmachine.h +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include "spimpl.h" - -namespace libscratchcpp -{ - -class VirtualMachinePrivate; - -/*! \brief A namespace for global variables and enumerations of the VirtualMachine. */ -namespace vm -{ - -enum Opcode -{ - OP_START, /*!< The first instruction of every bytecode. */ - OP_HALT, /*!< The last instruction of every bytecode. */ - OP_CONST, /*!< Adds a constant value with the index in the argument to the next register. */ - OP_NULL, /*!< Adds a null (zero) value to the next register. */ - OP_CHECKPOINT, /*!< A checkpoint for the VirtualMachine::moveToLastCheckpoint() method. */ - OP_IF, /*!< Jumps to the next instruction if the last register holds "true". If it's false, jumps to OP_ELSE or OP_ENDIF. */ - OP_ELSE, /*!< Jumps to OP_ENDIF. This instruction is typically reached when the if statement condition was "true". */ - OP_ENDIF, /*!< Doesn't do anything, but is used by OP_IF and OP_ELSE. */ - OP_FOREVER_LOOP, /*!< Runs a forever loop. */ - OP_REPEAT_LOOP, /*!< Runs a repeat loop with the number of periods stored in the last register. */ - OP_REPEAT_LOOP_INDEX, /*!< Returns the index of current repeat loop. */ - OP_REPEAT_LOOP_INDEX1, /*!< Returns the index of current repeat loop plus 1. */ - OP_UNTIL_LOOP, /*!< Evaluates the condition before OP_BEGIN_UNTIL_LOOP and runs a repeat until loop. */ - OP_BEGIN_UNTIL_LOOP, /*!< Used by OP_UNTIL_LOOP. */ - OP_LOOP_END, /*!< Ends the loop. */ - OP_PRINT, /*!< Prints the value stored in the last register. */ - OP_ADD, /*!< Adds the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_SUBTRACT, /*!< Subtracts the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_MULTIPLY, /*!< Multiplies the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_DIVIDE, /*!< Divides the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_MOD, /*!< Calculates module of the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_RANDOM, /*!< Generates a random value in the range stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_ROUND, /*!< Rounds the number in the last register and stores the result in the last register. */ - OP_ABS, /*!< Calculates the absolute value of the number in the last register and stores the result in the last register. */ - OP_FLOOR, /*!< Calculates the floor of the number in the last register and stores the result in the last register. */ - OP_CEIL, /*!< Calculates the ceiling of the number in the last register and stores the result in the last register. */ - OP_SQRT, /*!< Calculates the square root of the number in the last register and stores the result in the last register. */ - OP_SIN, /*!< Calculates the sine of the number in the last register and stores the result in the last register. */ - OP_COS, /*!< Calculates the cosine of the number in the last register and stores the result in the last register. */ - OP_TAN, /*!< Calculates the tangent of the number in the last register and stores the result in the last register. */ - OP_ASIN, /*!< Calculates the arcsine of the number in the last register and stores the result in the last register. */ - OP_ACOS, /*!< Calculates the arccosine of the number in the last register and stores the result in the last register. */ - OP_ATAN, /*!< Calculates the arctangent of the number in the last register and stores the result in the last register. */ - OP_GREATER_THAN, /*!< Compares (>) the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_LESS_THAN, /*!< Compares (<) the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_EQUALS, /*!< Compares (==) the values stored in the last 2 registers and stores the result in the last registry, deleting the input registers. */ - OP_AND, /*!< AND gate. The result is stored in the last registry and the input registers are deleted. */ - OP_OR, /*!< OR gate. The result is stored in the last registry and the input registers are deleted. */ - OP_NOT, /*!< NOT gate. The result is stored in the last registry and the input registers are deleted. */ - OP_SET_VAR, /*!< Sets the variable with the index in the argument to the value stored in the last register. */ - OP_CHANGE_VAR, /*!< Increments (or decrements) the variable with the index in the argument by the value stored in the last register. */ - OP_READ_VAR, /*!< Reads the variable with the index in the argument and stores the value in the last register. */ - OP_READ_LIST, /*!< Converts the list with the index in the argument to a string and stores the result in the last register. */ - OP_LIST_APPEND, /*!< Appens the value stored in the last register to the list with the index in the argument. */ - OP_LIST_DEL, /*!< Deletes the index (or item like "last" or "random") stored in the last register of the list with the index in the argument. */ - OP_LIST_DEL_ALL, /*!< Clears the list with the index in the argument. */ - OP_LIST_INSERT, /*!< Inserts the value from the second last register at the index (or item like "last" or "random") stored in the last register to the list with the index in the argument. */ - OP_LIST_REPLACE, /*!< Replaces the index (or item like "last" or "random") stored in the second last register with the value from the last register in the list with the index in the argument. */ - OP_LIST_GET_ITEM, /*!< Stores the value at the index (or item like "last" or "random") (of the list with the index in the argument) stored in the last register, in the last register. */ - OP_LIST_INDEX_OF, /*!< Stores the index of the value from the last register in the last register (of the list with the index in the argument). */ - OP_LIST_LENGTH, /*!< Stores the length of the list with the index in the argument, in the last register. */ - OP_LIST_CONTAINS, /*!< Stores true in the last register if the list with the index in the argument contains the value from the last register. */ - OP_STR_CONCAT, /*!< Concatenates the strings stored in the last 2 registers and stores the result in the last register, deleting the input registers. */ - OP_STR_AT, /*!< Stores the character at index in the last register of the string in the second last register, in the last register. */ - OP_STR_LENGTH, /*!< Stores the length of the string in the last register, in the last register. */ - OP_STR_CONTAINS, /*!< Stores true in the last register if the string stored in the second last register contains the substring in the last register. */ - OP_EXEC, /*!< Calls the function with the index in the argument. */ - OP_INIT_PROCEDURE, /*!< Initializes the list of procedure (custom block) arguments. */ - OP_CALL_PROCEDURE, /*!< Calls the procedure (custom block) with the index in the argument. */ - OP_ADD_ARG, /*!< Adds a procedure (custom block) argument with the value from the last register. */ - OP_READ_ARG, /*!< Reads the procedure (custom block) argument with the index in the argument and stores the value in the last register. */ - OP_BREAK_FRAME, /*!< Breaks current frame at the end of the loop. */ - OP_WARP /*!< Runs the script without screen refresh. */ -}; - -} - -class Target; -class IEngine; -class Script; -class Thread; -class List; - -/*! \brief The VirtualMachine class is a virtual machine for compiled Scratch scripts. */ -class LIBSCRATCHCPP_EXPORT VirtualMachine -{ - public: - VirtualMachine(); - VirtualMachine(Target *target, IEngine *engine, Script *script, Thread *thread = nullptr); - VirtualMachine(const VirtualMachine &) = delete; - - void setProcedures(unsigned int **procedures); - void setFunctions(BlockFunc *functions); - void setConstValues(const Value *values); - void setVariables(Value **variables); - void setLists(List **lists); - void setVariablesVector(const std::vector &variables); - void setListsVector(const std::vector &lists); - - void setBytecode(unsigned int *code); - - unsigned int **procedures() const; - const BlockFunc *functions() const; - const Value *constValues() const; - Value **variables() const; - List **lists() const; - - unsigned int *bytecode() const; - - size_t registerCount() const; - - Target *target() const; - IEngine *engine() const; - Script *script() const; - Thread *thread() const; - - const Value *getInput(unsigned int index, unsigned int argCount) const; - - void addReturnValue(const Value &v); - void replaceReturnValue(const Value &v, unsigned int offset); - - void run(); - void kill(); - void reset(); - void moveToLastCheckpoint(); - - void stop(bool savePos = true, bool breakFrame = false, bool goBack = false); - void promise(); - void resolvePromise(); - - bool atEnd() const; - - bool savePos() const; - - private: - spimpl::unique_impl_ptr impl; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt deleted file mode 100644 index d7416856..00000000 --- a/src/blocks/CMakeLists.txt +++ /dev/null @@ -1,115 +0,0 @@ -target_sources(scratchcpp - PRIVATE - blocks.cpp - blocks.h -) - -# Motion blocks -option(LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS "Motion blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_MOTION_BLOCKS) - target_sources(scratchcpp - PRIVATE - motionblocks.cpp - motionblocks.h - ) -endif() - -# Looks blocks -option(LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS "Looks blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_LOOKS_BLOCKS) - target_sources(scratchcpp - PRIVATE - looksblocks.cpp - looksblocks.h - ) -endif() - -# Sound blocks -option(LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS "Sound blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_SOUND_BLOCKS) - target_sources(scratchcpp - PRIVATE - soundblocks.cpp - soundblocks.h - ) -endif() - -# Event blocks -option(LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS "Event blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_EVENT_BLOCKS) - target_sources(scratchcpp - PRIVATE - eventblocks.cpp - eventblocks.h - ) -endif() - -# Control blocks -option(LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS "Control blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_CONTROL_BLOCKS) - target_sources(scratchcpp - PRIVATE - controlblocks.cpp - controlblocks.h - ) -endif() - -# Sensing blocks -option(LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS "Sensing blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_SENSING_BLOCKS) - target_sources(scratchcpp - PRIVATE - sensingblocks.cpp - sensingblocks.h - ) -endif() - -# Operator blocks -option(LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS "Operator blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_OPERATOR_BLOCKS) - target_sources(scratchcpp - PRIVATE - operatorblocks.cpp - operatorblocks.h - ) -endif() - -# Variable blocks -option(LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS "Variable blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_VARIABLE_BLOCKS) - target_sources(scratchcpp - PRIVATE - variableblocks.cpp - variableblocks.h - ) -endif() - -# List blocks -option(LIBSCRATCHCPP_ENABLE_LIST_BLOCKS "List blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_LIST_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_LIST_BLOCKS) - target_sources(scratchcpp - PRIVATE - listblocks.cpp - listblocks.h - ) -endif() - -# Custom blocks -option(LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS "Custom blocks support" ON) -if (LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS) - target_compile_definitions(scratchcpp PRIVATE ENABLE_CUSTOM_BLOCKS) - target_sources(scratchcpp - PRIVATE - customblocks.cpp - customblocks.h - ) -endif() diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp deleted file mode 100644 index a9d12f1e..00000000 --- a/src/blocks/blocks.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include - -#include "blocks.h" - -#ifdef ENABLE_MOTION_BLOCKS -#include "blocks/motionblocks.h" -#endif -#ifdef ENABLE_LOOKS_BLOCKS -#include "blocks/looksblocks.h" -#endif -#ifdef ENABLE_SOUND_BLOCKS -#include "blocks/soundblocks.h" -#endif -#ifdef ENABLE_EVENT_BLOCKS -#include "blocks/eventblocks.h" -#endif -#ifdef ENABLE_CONTROL_BLOCKS -#include "blocks/controlblocks.h" -#endif -#ifdef ENABLE_SENSING_BLOCKS -#include "blocks/sensingblocks.h" -#endif -#ifdef ENABLE_OPERATOR_BLOCKS -#include "blocks/operatorblocks.h" -#endif -#ifdef ENABLE_VARIABLE_BLOCKS -#include "blocks/variableblocks.h" -#endif -#ifdef ENABLE_LIST_BLOCKS -#include "blocks/listblocks.h" -#endif -#ifdef ENABLE_CUSTOM_BLOCKS -#include "blocks/customblocks.h" -#endif - -using namespace libscratchcpp; - -Blocks Blocks::m_instance; - -const std::vector> &Blocks::extensions() -{ - return m_instance.m_extensions; -} - -Blocks::Blocks() -{ -#ifdef ENABLE_MOTION_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_LOOKS_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_SOUND_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_EVENT_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_CONTROL_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_SENSING_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_OPERATOR_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_VARIABLE_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_LIST_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -#ifdef ENABLE_CUSTOM_BLOCKS - m_extensions.push_back(std::make_shared()); -#endif -} - -void Blocks::registerExtensions() -{ - for (auto ext : m_extensions) - ScratchConfiguration::registerExtension(ext); -} diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h deleted file mode 100644 index 56be7a34..00000000 --- a/src/blocks/blocks.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -namespace libscratchcpp -{ - -class IExtension; - -class Blocks -{ - public: - static const std::vector> &extensions(); - - private: - Blocks(); - void registerExtensions(); - - static Blocks m_instance; // use static initialization - std::vector> m_extensions; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/controlblocks.cpp b/src/blocks/controlblocks.cpp deleted file mode 100644 index ab5aab69..00000000 --- a/src/blocks/controlblocks.cpp +++ /dev/null @@ -1,302 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include - -#include "controlblocks.h" -#include "../engine/internal/clock.h" - -using namespace libscratchcpp; - -IClock *ControlBlocks::clock = nullptr; - -std::string ControlBlocks::name() const -{ - return "Control"; -} - -std::string ControlBlocks::description() const -{ - return name() + " blocks"; -} - -void ControlBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "control_forever", &compileRepeatForever); - engine->addCompileFunction(this, "control_repeat", &compileRepeat); - engine->addCompileFunction(this, "control_repeat_until", &compileRepeatUntil); - engine->addCompileFunction(this, "control_while", &compileRepeatWhile); - engine->addCompileFunction(this, "control_for_each", &compileRepeatForEach); - engine->addCompileFunction(this, "control_if", &compileIfStatement); - engine->addCompileFunction(this, "control_if_else", &compileIfElseStatement); - engine->addCompileFunction(this, "control_stop", &compileStop); - engine->addCompileFunction(this, "control_wait", &compileWait); - engine->addCompileFunction(this, "control_wait_until", &compileWaitUntil); - engine->addCompileFunction(this, "control_start_as_clone", &compileStartAsClone); - engine->addCompileFunction(this, "control_create_clone_of", &compileCreateClone); - engine->addCompileFunction(this, "control_delete_this_clone", &compileDeleteThisClone); - - // Inputs - engine->addInput(this, "SUBSTACK", SUBSTACK); - engine->addInput(this, "SUBSTACK2", SUBSTACK2); - engine->addInput(this, "TIMES", TIMES); - engine->addInput(this, "CONDITION", CONDITION); - engine->addInput(this, "DURATION", DURATION); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "CLONE_OPTION", CLONE_OPTION); - - // Fields - engine->addField(this, "STOP_OPTION", STOP_OPTION); - engine->addField(this, "VARIABLE", VARIABLE); - - // Field values - engine->addFieldValue(this, "all", StopAll); - engine->addFieldValue(this, "this script", StopThisScript); - engine->addFieldValue(this, "other scripts in sprite", StopOtherScriptsInSprite); - engine->addFieldValue(this, "other scripts in stage", StopOtherScriptsInSprite); -} - -void ControlBlocks::compileRepeatForever(Compiler *compiler) -{ - compiler->addInstruction(vm::OP_FOREVER_LOOP); - compiler->moveToSubstack(compiler->inputBlock(SUBSTACK), Compiler::SubstackType::Loop); -} - -void ControlBlocks::compileRepeat(Compiler *compiler) -{ - auto substack = compiler->inputBlock(SUBSTACK); - if (substack) { - compiler->addInput(TIMES); - compiler->addInstruction(vm::OP_REPEAT_LOOP); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); - } -} - -void ControlBlocks::compileRepeatUntil(Compiler *compiler) -{ - auto substack = compiler->inputBlock(SUBSTACK); - compiler->addInstruction(vm::OP_UNTIL_LOOP); - compiler->addInput(CONDITION); - compiler->addInstruction(vm::OP_BEGIN_UNTIL_LOOP); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); -} - -void ControlBlocks::compileRepeatWhile(Compiler *compiler) -{ - auto substack = compiler->inputBlock(SUBSTACK); - compiler->addInstruction(vm::OP_UNTIL_LOOP); - compiler->addInput(CONDITION); - compiler->addInstruction(vm::OP_NOT); - compiler->addInstruction(vm::OP_BEGIN_UNTIL_LOOP); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); -} - -void ControlBlocks::compileRepeatForEach(Compiler *compiler) -{ - compiler->addInput(VALUE); - auto substack = compiler->inputBlock(SUBSTACK); - if (substack) { - compiler->addInstruction(vm::OP_REPEAT_LOOP); - compiler->addInstruction(vm::OP_REPEAT_LOOP_INDEX1); - compiler->addInstruction(vm::OP_SET_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); - compiler->moveToSubstack(substack, Compiler::SubstackType::Loop); - } else - compiler->addInstruction(vm::OP_SET_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); -} - -void ControlBlocks::compileIfStatement(Compiler *compiler) -{ - auto substack = compiler->inputBlock(SUBSTACK); - if (substack) { - compiler->addInput(CONDITION); - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack, Compiler::SubstackType::IfStatement); - } -} - -void ControlBlocks::compileIfElseStatement(Compiler *compiler) -{ - auto substack1 = compiler->inputBlock(SUBSTACK); - auto substack2 = compiler->inputBlock(SUBSTACK2); - if (substack1 || substack2) - compiler->addInput(CONDITION); - if (substack1 && substack2) { - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack1, substack2, Compiler::SubstackType::IfStatement); - } else if (substack1) { - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack1, Compiler::SubstackType::IfStatement); - } else if (substack2) { - compiler->addInstruction(vm::OP_NOT); - compiler->addInstruction(vm::OP_IF); - compiler->moveToSubstack(substack2, Compiler::SubstackType::IfStatement); - } -} - -void ControlBlocks::compileStop(Compiler *compiler) -{ - int option = compiler->field(STOP_OPTION)->specialValueId(); - switch (option) { - case StopAll: - compiler->addFunctionCall(&stopAll); - compiler->addInstruction(vm::OP_HALT); - break; - - case StopThisScript: - compiler->addInstruction(vm::OP_HALT); - break; - - case StopOtherScriptsInSprite: - compiler->addFunctionCall(&stopOtherScriptsInSprite); - break; - } -} - -void ControlBlocks::compileWait(Compiler *compiler) -{ - compiler->addInput(DURATION); - compiler->addFunctionCall(&startWait); - compiler->addFunctionCall(&wait); -} - -void ControlBlocks::compileWaitUntil(Compiler *compiler) -{ - compiler->addInstruction(vm::OP_CHECKPOINT); - compiler->addInput(CONDITION); - compiler->addFunctionCall(&waitUntil); -} - -void ControlBlocks::compileStartAsClone(Compiler *compiler) -{ - compiler->engine()->addCloneInitScript(compiler->block()); -} - -void ControlBlocks::compileCreateClone(Compiler *compiler) -{ - Input *input = compiler->input(CLONE_OPTION); - - if (input->pointsToDropdownMenu()) { - std::string spriteName = input->selectedMenuItem(); - - if (spriteName == "_myself_") - compiler->addFunctionCall(&createCloneOfMyself); - else { - int index = compiler->engine()->findTarget(spriteName); - compiler->addConstValue(index); - compiler->addFunctionCall(&createCloneByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&createClone); - } -} - -void ControlBlocks::compileDeleteThisClone(Compiler *compiler) -{ - compiler->addFunctionCall(&deleteThisClone); -} - -unsigned int ControlBlocks::stopAll(VirtualMachine *vm) -{ - vm->engine()->stop(); - return 0; -} - -unsigned int ControlBlocks::stopOtherScriptsInSprite(VirtualMachine *vm) -{ - vm->engine()->stopTarget(vm->target(), vm->thread()); - return 0; -} - -unsigned int ControlBlocks::startWait(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - m_timeMap[vm] = { currentTime, vm->getInput(0, 1)->toDouble() * 1000 }; - vm->engine()->requestRedraw(); - - return 1; -} - -unsigned int ControlBlocks::wait(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - assert(m_timeMap.count(vm) == 1); - if (std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count() >= m_timeMap[vm].second) { - m_timeMap.erase(vm); - vm->stop(true, true, false); - } else - vm->stop(true, true, true); - return 0; -} - -unsigned int ControlBlocks::waitUntil(VirtualMachine *vm) -{ - if (!vm->getInput(0, 1)->toBool()) { - vm->moveToLastCheckpoint(); - vm->stop(true, true, false); - } - return 1; -} - -unsigned int ControlBlocks::createClone(VirtualMachine *vm) -{ - std::string spriteName = vm->getInput(0, 1)->toString(); - Target *target; - - if (spriteName == "_myself_") - target = vm->target(); - else - target = vm->engine()->targetAt(vm->engine()->findTarget(spriteName)); - - Sprite *sprite = dynamic_cast(target); - - if (sprite) - sprite->clone(); - - return 1; -} - -unsigned int ControlBlocks::createCloneByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - sprite->clone(); - - return 1; -} - -unsigned int ControlBlocks::createCloneOfMyself(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->clone(); - - return 0; -} - -unsigned int ControlBlocks::deleteThisClone(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite && sprite->isClone()) { - vm->engine()->stopTarget(sprite, nullptr); - sprite->deleteClone(); - } - - return 0; -} diff --git a/src/blocks/controlblocks.h b/src/blocks/controlblocks.h deleted file mode 100644 index aa0c8710..00000000 --- a/src/blocks/controlblocks.h +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include - -namespace libscratchcpp -{ - -class Compiler; -class VirtualMachine; -class IClock; - -/*! \brief The ControlBlocks class contains the implementation of control blocks. */ -class ControlBlocks : public IExtension -{ - public: - enum Inputs - { - SUBSTACK, - SUBSTACK2, - TIMES, - CONDITION, - DURATION, - VALUE, - CLONE_OPTION - }; - - enum Fields - { - STOP_OPTION, - VARIABLE - }; - - enum FieldValues - { - StopAll, - StopThisScript, - StopOtherScriptsInSprite - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - static void compileRepeatForever(Compiler *compiler); - static void compileRepeat(Compiler *compiler); - static void compileRepeatUntil(Compiler *compiler); - static void compileRepeatWhile(Compiler *compiler); - static void compileRepeatForEach(Compiler *compiler); - static void compileIfStatement(Compiler *compiler); - static void compileIfElseStatement(Compiler *compiler); - static void compileStop(Compiler *compiler); - static void compileWait(Compiler *compiler); - static void compileWaitUntil(Compiler *compiler); - static void compileStartAsClone(Compiler *compiler); - static void compileCreateClone(Compiler *compiler); - static void compileDeleteThisClone(Compiler *compiler); - - static unsigned int stopAll(VirtualMachine *vm); - static unsigned int stopOtherScriptsInSprite(VirtualMachine *vm); - static unsigned int startWait(VirtualMachine *vm); - static unsigned int wait(VirtualMachine *vm); - static unsigned int waitUntil(VirtualMachine *vm); - static unsigned int createClone(VirtualMachine *vm); - static unsigned int createCloneByIndex(VirtualMachine *vm); - static unsigned int createCloneOfMyself(VirtualMachine *vm); - static unsigned int deleteThisClone(VirtualMachine *vm); - - static inline std::unordered_map> m_timeMap; - - static IClock *clock; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/customblocks.cpp b/src/blocks/customblocks.cpp deleted file mode 100644 index aee3b757..00000000 --- a/src/blocks/customblocks.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include - -#include "customblocks.h" - -using namespace libscratchcpp; - -std::string CustomBlocks::name() const -{ - return "Custom blocks"; -} - -std::string CustomBlocks::description() const -{ - return name(); -} - -void CustomBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "procedures_definition", &compileDefinition); - engine->addCompileFunction(this, "procedures_call", &compileCall); - engine->addCompileFunction(this, "argument_reporter_boolean", &compileArgument); - engine->addCompileFunction(this, "argument_reporter_string_number", &compileArgument); - - // Inputs - engine->addInput(this, "custom_block", CUSTOM_BLOCK); - - // Fields - engine->addField(this, "VALUE", VALUE); -} - -void CustomBlocks::compileDefinition(Compiler *compiler) -{ - auto prototype = compiler->input(CUSTOM_BLOCK)->valueBlock()->mutationPrototype(); - compiler->setProcedurePrototype(prototype); - if (prototype->warp()) - compiler->warp(); - const std::vector &args = prototype->argumentNames(); - const std::string &code = prototype->procCode(); - for (const std::string &arg : args) - compiler->addProcedureArg(code, arg); -} - -void CustomBlocks::compileCall(Compiler *compiler) -{ - compiler->addInstruction(vm::OP_INIT_PROCEDURE); - auto block = compiler->block(); - auto prototype = block->mutationPrototype(); - const std::vector &args = prototype->argumentIds(); - size_t i = 0; - for (const std::string &id : args) { - auto index = block->findInput(id); - if (index == -1) - compiler->addInstruction(vm::OP_NULL); - else - compiler->addInput(block->inputAt(index).get()); - compiler->addInstruction(vm::OP_ADD_ARG); - i++; - } - const std::string &code = prototype->procCode(); - compiler->addInstruction(vm::OP_CALL_PROCEDURE, { compiler->procedureIndex(code) }); -} - -void CustomBlocks::compileArgument(Compiler *compiler) -{ - if (compiler->procedurePrototype()) { - const std::string &argName = compiler->field(VALUE)->value().toString(); - auto index = compiler->procedureArgIndex(compiler->procedurePrototype()->procCode(), argName); - if (index == -1) - compiler->addInstruction(vm::OP_NULL); - else - compiler->addInstruction(vm::OP_READ_ARG, { static_cast(index) }); - } else - compiler->addInstruction(vm::OP_NULL); -} diff --git a/src/blocks/customblocks.h b/src/blocks/customblocks.h deleted file mode 100644 index a40e37c3..00000000 --- a/src/blocks/customblocks.h +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class Compiler; - -class CustomBlocks : public IExtension -{ - public: - enum Inputs - { - CUSTOM_BLOCK - }; - - enum Fields - { - VALUE - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - static void compileDefinition(Compiler *compiler); - static void compileCall(Compiler *compiler); - static void compileArgument(Compiler *compiler); -}; - -} // namespace libscratchcpp diff --git a/src/blocks/eventblocks.cpp b/src/blocks/eventblocks.cpp deleted file mode 100644 index 757e2bda..00000000 --- a/src/blocks/eventblocks.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "eventblocks.h" -#include "audio/audioinput.h" -#include "audio/iaudioloudness.h" - -using namespace libscratchcpp; - -IAudioInput *EventBlocks::audioInput = nullptr; - -std::string EventBlocks::name() const -{ - return "Events"; -} - -std::string libscratchcpp::EventBlocks::description() const -{ - return "Event blocks"; -} - -void EventBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject); - engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked); - engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked); - engine->addCompileFunction(this, "event_whenstageclicked", &compileWhenStageClicked); - engine->addCompileFunction(this, "event_broadcast", &compileBroadcast); - engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait); - engine->addCompileFunction(this, "event_whenbroadcastreceived", &compileWhenBroadcastReceived); - engine->addCompileFunction(this, "event_whenbackdropswitchesto", &compileWhenBackdropSwitchesTo); - engine->addCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThan); - engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed); - - // Hat predicates - engine->addHatPredicateCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObjectPredicate); - engine->addHatPredicateCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThanPredicate); - - // Inputs - engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU); - engine->addInput(this, "BROADCAST_INPUT", BROADCAST_INPUT); - engine->addInput(this, "VALUE", VALUE); - - // Fields - engine->addField(this, "BROADCAST_OPTION", BROADCAST_OPTION); - engine->addField(this, "BACKDROP", BACKDROP); - engine->addField(this, "WHENGREATERTHANMENU", WHENGREATERTHANMENU); - engine->addField(this, "KEY_OPTION", KEY_OPTION); - - // Fields values - engine->addFieldValue(this, "LOUDNESS", Loudness); - engine->addFieldValue(this, "TIMER", Timer); -} - -void EventBlocks::compileWhenTouchingObjectPredicate(Compiler *compiler) -{ - Input *input = compiler->input(TOUCHINGOBJECTMENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - compiler->addConstValue(value); - compiler->addFunctionCall(&whenTouchingObjectPredicate); - } else { - compiler->addInput(input); - compiler->addFunctionCall(&whenTouchingObjectPredicate); - } -} - -void EventBlocks::compileWhenTouchingObject(Compiler *compiler) -{ - compiler->engine()->addWhenTouchingObjectScript(compiler->block()); -} - -void EventBlocks::compileWhenFlagClicked(Compiler *compiler) -{ - compiler->engine()->addGreenFlagScript(compiler->block()); -} - -void EventBlocks::compileWhenThisSpriteClicked(Compiler *compiler) -{ - compiler->engine()->addTargetClickScript(compiler->block()); -} - -void EventBlocks::compileWhenStageClicked(Compiler *compiler) -{ - compiler->engine()->addTargetClickScript(compiler->block()); -} - -void EventBlocks::compileBroadcast(Compiler *compiler) -{ - compiler->addInput(BROADCAST_INPUT); - compiler->addFunctionCall(&broadcast); -} - -void EventBlocks::compileBroadcastAndWait(Compiler *compiler) -{ - compiler->addInput(BROADCAST_INPUT); - compiler->addFunctionCall(&broadcastAndWait); -} - -void EventBlocks::compileWhenBroadcastReceived(Compiler *compiler) -{ - Field *field = compiler->field(BROADCAST_OPTION); - auto broadcast = std::static_pointer_cast(field->valuePtr()); - - compiler->engine()->addBroadcastScript(compiler->block(), field, broadcast.get()); -} - -void EventBlocks::compileWhenBackdropSwitchesTo(Compiler *compiler) -{ - compiler->engine()->addBackdropChangeScript(compiler->block(), compiler->field(BACKDROP)); -} - -void EventBlocks::compileWhenGreaterThanPredicate(Compiler *compiler) -{ - Field *field = compiler->field(WHENGREATERTHANMENU); - BlockFunc predicate = nullptr; - - if (field) { - switch (field->specialValueId()) { - case Loudness: - predicate = &whenLoudnessGreaterThanPredicate; - break; - - case Timer: - predicate = &whenTimerGreaterThanPredicate; - break; - - default: - compiler->addInstruction(vm::OP_NULL); - return; - } - } - - if (predicate) { - compiler->addInput(VALUE); - compiler->addFunctionCall(predicate); - } -} - -void EventBlocks::compileWhenGreaterThan(Compiler *compiler) -{ - compiler->engine()->addWhenGreaterThanScript(compiler->block()); -} - -void EventBlocks::compileWhenKeyPressed(Compiler *compiler) -{ - // NOTE: Field values don't have to be registered because keys are referenced by their names - compiler->engine()->addKeyPressScript(compiler->block(), compiler->field(KEY_OPTION)); -} - -unsigned int EventBlocks::whenTouchingObjectPredicate(VirtualMachine *vm) -{ - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1); - else if (value == "_edge_") - vm->replaceReturnValue(vm->target()->touchingEdge(), 1); - else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - - if (target && !target->isStage()) - vm->replaceReturnValue(vm->target()->touchingSprite(static_cast(target)), 1); - else - vm->replaceReturnValue(false, 1); - } - - return 0; -} - -unsigned int EventBlocks::broadcast(VirtualMachine *vm) -{ - std::vector broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString()); - - for (int index : broadcasts) - vm->engine()->broadcast(index, vm->thread(), false); - - return 1; -} - -unsigned int EventBlocks::broadcastAndWait(VirtualMachine *vm) -{ - std::vector broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString()); - - for (int index : broadcasts) - vm->engine()->broadcast(index, vm->thread(), true); - - vm->promise(); - - return 1; -} - -unsigned int EventBlocks::whenLoudnessGreaterThanPredicate(VirtualMachine *vm) -{ - if (!audioInput) - audioInput = AudioInput::instance().get(); - - auto audioLoudness = audioInput->audioLoudness(); - const Value &operand = *vm->getInput(0, 1); - vm->replaceReturnValue(Value(audioLoudness->getLoudness()) > operand, 1); - return 0; -} - -unsigned int EventBlocks::whenTimerGreaterThanPredicate(VirtualMachine *vm) -{ - const Value &operand = *vm->getInput(0, 1); - vm->replaceReturnValue(Value(vm->engine()->timer()->value()) > operand, 1); - return 0; -} diff --git a/src/blocks/eventblocks.h b/src/blocks/eventblocks.h deleted file mode 100644 index e4d28f4a..00000000 --- a/src/blocks/eventblocks.h +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class Compiler; -class VirtualMachine; -class IAudioInput; - -/*! \brief The EventBlocks class contains the implementation of event blocks. */ -class EventBlocks : public IExtension -{ - public: - enum Inputs - { - TOUCHINGOBJECTMENU, - BROADCAST_INPUT, - VALUE - }; - - enum Fields - { - BROADCAST_OPTION, - BACKDROP, - WHENGREATERTHANMENU, - KEY_OPTION - }; - - enum FieldValues - { - Loudness, - Timer - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - static void compileWhenTouchingObjectPredicate(Compiler *compiler); - static void compileWhenTouchingObject(Compiler *compiler); - static void compileWhenFlagClicked(Compiler *compiler); - static void compileWhenThisSpriteClicked(Compiler *compiler); - static void compileWhenStageClicked(Compiler *compiler); - static void compileBroadcast(Compiler *compiler); - static void compileBroadcastAndWait(Compiler *compiler); - static void compileWhenBroadcastReceived(Compiler *compiler); - static void compileWhenBackdropSwitchesTo(Compiler *compiler); - static void compileWhenGreaterThanPredicate(Compiler *compiler); - static void compileWhenGreaterThan(Compiler *compiler); - static void compileWhenKeyPressed(Compiler *compiler); - - static unsigned int whenTouchingObjectPredicate(VirtualMachine *vm); - - static unsigned int broadcast(VirtualMachine *vm); - static unsigned int broadcastAndWait(VirtualMachine *vm); - - static unsigned int whenLoudnessGreaterThanPredicate(VirtualMachine *vm); - static unsigned int whenTimerGreaterThanPredicate(VirtualMachine *vm); - - static IAudioInput *audioInput; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/listblocks.cpp b/src/blocks/listblocks.cpp deleted file mode 100644 index 135dd0bd..00000000 --- a/src/blocks/listblocks.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "listblocks.h" - -using namespace libscratchcpp; - -std::string ListBlocks::name() const -{ - return "Lists"; -} - -std::string ListBlocks::description() const -{ - return "List blocks"; -} - -void ListBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "data_listcontents", &compileListContents); - engine->addCompileFunction(this, "data_addtolist", &compileAddToList); - engine->addCompileFunction(this, "data_deleteoflist", &compileDeleteFromList); - engine->addCompileFunction(this, "data_deletealloflist", &compileDeleteAllOfList); - engine->addCompileFunction(this, "data_insertatlist", &compileInsertToList); - engine->addCompileFunction(this, "data_replaceitemoflist", &compileReplaceItemOfList); - engine->addCompileFunction(this, "data_itemoflist", &compileItemOfList); - engine->addCompileFunction(this, "data_itemnumoflist", &compileItemNumberInList); - engine->addCompileFunction(this, "data_lengthoflist", &compileLengthOfList); - engine->addCompileFunction(this, "data_listcontainsitem", &compileListContainsItem); - engine->addCompileFunction(this, "data_showlist", &compileShowList); - engine->addCompileFunction(this, "data_hidelist", &compileHideList); - - // Monitor names - engine->addMonitorNameFunction(this, "data_listcontents", &listContentsMonitorName); - - // Inputs - engine->addInput(this, "ITEM", ITEM); - engine->addInput(this, "INDEX", INDEX); - - // Fields - engine->addField(this, "LIST", LIST); -} - -void ListBlocks::compileListContents(Compiler *compiler) -{ - // NOTE: This block is only used by list monitors - // Instead of returning the actual list contents, let's just return the index of the list - // and let the renderer read the list using the index - compiler->addConstValue(static_cast(compiler->listIndex(compiler->field(LIST)->valuePtr()))); -} - -void ListBlocks::compileAddToList(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_APPEND, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileDeleteFromList(Compiler *compiler) -{ - compiler->addInput(INDEX); - compiler->addInstruction(vm::OP_LIST_DEL, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileDeleteAllOfList(Compiler *compiler) -{ - compiler->addInstruction(vm::OP_LIST_DEL_ALL, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileInsertToList(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInput(INDEX); - compiler->addInstruction(vm::OP_LIST_INSERT, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileReplaceItemOfList(Compiler *compiler) -{ - compiler->addInput(INDEX); - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_REPLACE, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileItemOfList(Compiler *compiler) -{ - compiler->addInput(INDEX); - compiler->addInstruction(vm::OP_LIST_GET_ITEM, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileItemNumberInList(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_INDEX_OF, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileLengthOfList(Compiler *compiler) -{ - compiler->addInstruction(vm::OP_LIST_LENGTH, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileListContainsItem(Compiler *compiler) -{ - compiler->addInput(ITEM); - compiler->addInstruction(vm::OP_LIST_CONTAINS, { compiler->listIndex(compiler->field(LIST)->valuePtr()) }); -} - -void ListBlocks::compileShowList(Compiler *compiler) -{ - Field *field = compiler->field(LIST); - assert(field); - List *var = static_cast(field->valuePtr().get()); - assert(var); - - compiler->addConstValue(var->id()); - - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&showGlobalList); - else - compiler->addFunctionCall(&showList); -} - -void ListBlocks::compileHideList(Compiler *compiler) -{ - Field *field = compiler->field(LIST); - assert(field); - List *var = static_cast(field->valuePtr().get()); - assert(var); - - compiler->addConstValue(var->id()); - - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&hideGlobalList); - else - compiler->addFunctionCall(&hideList); -} - -void ListBlocks::setListVisible(std::shared_ptr list, bool visible, IEngine *engine) -{ - if (list) { - Monitor *monitor = list->monitor(); - - if (!monitor) - monitor = engine->createListMonitor(list, "data_listcontents", "LIST", LIST, &compileListContents); - - monitor->setVisible(visible); - } -} - -unsigned int ListBlocks::showGlobalList(VirtualMachine *vm) -{ - if (Stage *target = vm->engine()->stage()) { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), true, vm->engine()); - } - - return 1; -} - -unsigned int ListBlocks::showList(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root list - int index = sprite->findListById(vm->getInput(0, 1)->toString()); - setListVisible(sprite->listAt(index), true, vm->engine()); - } else { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), true, vm->engine()); - } - } - - return 1; -} - -unsigned int ListBlocks::hideGlobalList(VirtualMachine *vm) -{ - if (Stage *target = vm->engine()->stage()) { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), false, vm->engine()); - } - - return 1; -} - -unsigned int ListBlocks::hideList(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root list - int index = sprite->findListById(vm->getInput(0, 1)->toString()); - setListVisible(sprite->listAt(index), false, vm->engine()); - } else { - int index = target->findListById(vm->getInput(0, 1)->toString()); - setListVisible(target->listAt(index), false, vm->engine()); - } - } - - return 1; -} - -const std::string &ListBlocks::listContentsMonitorName(Block *block) -{ - List *list = dynamic_cast(block->findFieldById(LIST)->valuePtr().get()); - - if (list) - return list->name(); - else { - static const std::string empty = ""; - return empty; - } -} diff --git a/src/blocks/listblocks.h b/src/blocks/listblocks.h deleted file mode 100644 index 69e49aa3..00000000 --- a/src/blocks/listblocks.h +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -namespace libscratchcpp -{ - -class Compiler; -class List; - -/*! \brief The ListBlocks class contains the implementation of list blocks. */ -class ListBlocks : public IExtension -{ - public: - enum Inputs - { - ITEM, - INDEX - }; - - enum Fields - { - LIST - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - static void compileListContents(Compiler *compiler); - static void compileAddToList(Compiler *compiler); - static void compileDeleteFromList(Compiler *compiler); - static void compileDeleteAllOfList(Compiler *compiler); - static void compileInsertToList(Compiler *compiler); - static void compileReplaceItemOfList(Compiler *compiler); - static void compileItemOfList(Compiler *compiler); - static void compileItemNumberInList(Compiler *compiler); - static void compileLengthOfList(Compiler *compiler); - static void compileListContainsItem(Compiler *compiler); - static void compileShowList(Compiler *compiler); - static void compileHideList(Compiler *compiler); - - static void setListVisible(std::shared_ptr list, bool visible, IEngine *engine); - - static unsigned int showGlobalList(VirtualMachine *vm); - static unsigned int showList(VirtualMachine *vm); - static unsigned int hideGlobalList(VirtualMachine *vm); - static unsigned int hideList(VirtualMachine *vm); - - static const std::string &listContentsMonitorName(Block *block); - - private: - static int validateIndex(size_t index, size_t listLength); -}; - -} // namespace libscratchcpp diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp deleted file mode 100644 index f142f34c..00000000 --- a/src/blocks/looksblocks.cpp +++ /dev/null @@ -1,1102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "looksblocks.h" -#include "../engine/internal/randomgenerator.h" -#include "../engine/internal/clock.h" - -using namespace libscratchcpp; - -IRandomGenerator *LooksBlocks::rng = nullptr; -IClock *LooksBlocks::clock = nullptr; - -// TODO: Use C++20 -template -void erase_if(ContainerT &items, const PredicateT &predicate) -{ - for (auto it = items.begin(); it != items.end();) { - if (predicate(*it)) - it = items.erase(it); - else - ++it; - } -} - -std::string LooksBlocks::name() const -{ - return "Looks"; -} - -std::string LooksBlocks::description() const -{ - return name() + " blocks"; -} - -void LooksBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "looks_sayforsecs", &compileSayForSecs); - engine->addCompileFunction(this, "looks_say", &compileSay); - engine->addCompileFunction(this, "looks_thinkforsecs", &compileThinkForSecs); - engine->addCompileFunction(this, "looks_think", &compileThink); - engine->addCompileFunction(this, "looks_show", &compileShow); - engine->addCompileFunction(this, "looks_hide", &compileHide); - engine->addCompileFunction(this, "looks_changeeffectby", &compileChangeEffectBy); - engine->addCompileFunction(this, "looks_seteffectto", &compileSetEffectTo); - engine->addCompileFunction(this, "looks_cleargraphiceffects", &compileClearGraphicEffects); - engine->addCompileFunction(this, "looks_changesizeby", &compileChangeSizeBy); - engine->addCompileFunction(this, "looks_setsizeto", &compileSetSizeTo); - engine->addCompileFunction(this, "looks_size", &compileSize); - engine->addCompileFunction(this, "looks_switchcostumeto", &compileSwitchCostumeTo); - engine->addCompileFunction(this, "looks_nextcostume", &compileNextCostume); - engine->addCompileFunction(this, "looks_switchbackdropto", &compileSwitchBackdropTo); - engine->addCompileFunction(this, "looks_switchbackdroptoandwait", &compileSwitchBackdropToAndWait); - engine->addCompileFunction(this, "looks_nextbackdrop", &compileNextBackdrop); - engine->addCompileFunction(this, "looks_gotofrontback", &compileGoToFrontBack); - engine->addCompileFunction(this, "looks_goforwardbackwardlayers", &compileGoForwardBackwardLayers); - engine->addCompileFunction(this, "looks_costumenumbername", &compileCostumeNumberName); - engine->addCompileFunction(this, "looks_backdropnumbername", &compileBackdropNumberName); - - // Monitor names - engine->addMonitorNameFunction(this, "looks_costumenumbername", &costumeNumberNameMonitorName); - engine->addMonitorNameFunction(this, "looks_backdropnumbername", &backdropNumberNameMonitorName); - engine->addMonitorNameFunction(this, "looks_size", &sizeMonitorName); - - // Inputs - engine->addInput(this, "MESSAGE", MESSAGE); - engine->addInput(this, "SECS", SECS); - engine->addInput(this, "CHANGE", CHANGE); - engine->addInput(this, "SIZE", SIZE); - engine->addInput(this, "COSTUME", COSTUME); - engine->addInput(this, "BACKDROP", BACKDROP); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "NUM", NUM); - - // Fields - engine->addField(this, "NUMBER_NAME", NUMBER_NAME); - engine->addField(this, "EFFECT", EFFECT); - engine->addField(this, "FRONT_BACK", FRONT_BACK); - engine->addField(this, "FORWARD_BACKWARD", FORWARD_BACKWARD); - - // Field values - engine->addFieldValue(this, "number", Number); - engine->addFieldValue(this, "name", Name); - engine->addFieldValue(this, "COLOR", ColorEffect); - engine->addFieldValue(this, "FISHEYE", FisheyeEffect); - engine->addFieldValue(this, "WHIRL", WhirlEffect); - engine->addFieldValue(this, "PIXELATE", PixelateEffect); - engine->addFieldValue(this, "MOSAIC", MosaicEffect); - engine->addFieldValue(this, "BRIGHTNESS", BrightnessEffect); - engine->addFieldValue(this, "GHOST", GhostEffect); - engine->addFieldValue(this, "front", Front); - engine->addFieldValue(this, "back", Back); - engine->addFieldValue(this, "forward", Forward); - engine->addFieldValue(this, "backward", Backward); -} - -void LooksBlocks::onInit(IEngine *engine) -{ - engine->threadAboutToStop().connect([](Thread *thread) { - m_timeMap.erase(thread->vm()); - erase_if(m_waitingBubbles, [thread](const std::pair &pair) { return pair.second == thread->vm(); }); - }); - - engine->stopped().connect([engine]() { - const auto &targets = engine->targets(); - - for (auto target : targets) { - target->bubble()->setText(""); - target->clearGraphicsEffects(); - } - }); -} - -void LooksBlocks::compileSayForSecs(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addInput(SECS); - compiler->addFunctionCall(&startSayForSecs); - compiler->addFunctionCall(&sayForSecs); -} - -void LooksBlocks::compileSay(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addFunctionCall(&say); -} - -void LooksBlocks::compileThinkForSecs(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addInput(SECS); - compiler->addFunctionCall(&startThinkForSecs); - compiler->addFunctionCall(&thinkForSecs); -} - -void LooksBlocks::compileThink(Compiler *compiler) -{ - compiler->addInput(MESSAGE); - compiler->addFunctionCall(&think); -} - -void LooksBlocks::compileShow(Compiler *compiler) -{ - compiler->addFunctionCall(&show); -} - -void LooksBlocks::compileHide(Compiler *compiler) -{ - compiler->addFunctionCall(&hide); -} - -void LooksBlocks::compileChangeEffectBy(Compiler *compiler) -{ - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case ColorEffect: - if (!m_colorEffect) - m_colorEffect = ScratchConfiguration::getGraphicsEffect("color"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeColorEffectBy); - break; - - case FisheyeEffect: - if (!m_fisheyeEffect) - m_fisheyeEffect = ScratchConfiguration::getGraphicsEffect("fisheye"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeFisheyeEffectBy); - break; - - case WhirlEffect: - if (!m_whirlEffect) - m_whirlEffect = ScratchConfiguration::getGraphicsEffect("whirl"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeWhirlEffectBy); - break; - - case PixelateEffect: - if (!m_pixelateEffect) - m_pixelateEffect = ScratchConfiguration::getGraphicsEffect("pixelate"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changePixelateEffectBy); - break; - - case MosaicEffect: - if (!m_mosaicEffect) - m_mosaicEffect = ScratchConfiguration::getGraphicsEffect("mosaic"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeMosaicEffectBy); - break; - - case BrightnessEffect: - if (!m_brightnessEffect) - m_brightnessEffect = ScratchConfiguration::getGraphicsEffect("brightness"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeBrightnessEffectBy); - break; - - case GhostEffect: - if (!m_ghostEffect) - m_ghostEffect = ScratchConfiguration::getGraphicsEffect("ghost"); - - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeGhostEffectBy); - break; - - default: - IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect(compiler->field(EFFECT)->value().toString()); - - if (effect) { - auto it = std::find(m_customGraphicsEffects.begin(), m_customGraphicsEffects.end(), effect); - size_t index; - - if (it == m_customGraphicsEffects.end()) { - index = m_customGraphicsEffects.size(); - m_customGraphicsEffects.push_back(effect); - } else - index = it - m_customGraphicsEffects.begin(); - - compiler->addConstValue(index); - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeEffectBy); - } - - break; - } -} - -void LooksBlocks::compileSetEffectTo(Compiler *compiler) -{ - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case ColorEffect: - if (!m_colorEffect) - m_colorEffect = ScratchConfiguration::getGraphicsEffect("color"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setColorEffectTo); - break; - - case FisheyeEffect: - if (!m_fisheyeEffect) - m_fisheyeEffect = ScratchConfiguration::getGraphicsEffect("fisheye"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setFisheyeEffectTo); - break; - - case WhirlEffect: - if (!m_whirlEffect) - m_whirlEffect = ScratchConfiguration::getGraphicsEffect("whirl"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setWhirlEffectTo); - break; - - case PixelateEffect: - if (!m_pixelateEffect) - m_pixelateEffect = ScratchConfiguration::getGraphicsEffect("pixelate"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setPixelateEffectTo); - break; - - case MosaicEffect: - if (!m_mosaicEffect) - m_mosaicEffect = ScratchConfiguration::getGraphicsEffect("mosaic"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setMosaicEffectTo); - break; - - case BrightnessEffect: - if (!m_brightnessEffect) - m_brightnessEffect = ScratchConfiguration::getGraphicsEffect("brightness"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setBrightnessEffectTo); - break; - - case GhostEffect: - if (!m_ghostEffect) - m_ghostEffect = ScratchConfiguration::getGraphicsEffect("ghost"); - - compiler->addInput(VALUE); - compiler->addFunctionCall(&setGhostEffectTo); - break; - - default: - IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect(compiler->field(EFFECT)->value().toString()); - - if (effect) { - auto it = std::find(m_customGraphicsEffects.begin(), m_customGraphicsEffects.end(), effect); - size_t index; - - if (it == m_customGraphicsEffects.end()) { - index = m_customGraphicsEffects.size(); - m_customGraphicsEffects.push_back(effect); - } else - index = it - m_customGraphicsEffects.begin(); - - compiler->addConstValue(index); - compiler->addInput(VALUE); - compiler->addFunctionCall(&setEffectTo); - } - - break; - } -} - -void LooksBlocks::compileClearGraphicEffects(Compiler *compiler) -{ - compiler->addFunctionCall(&clearGraphicEffects); -} - -void LooksBlocks::compileChangeSizeBy(Compiler *compiler) -{ - compiler->addInput(CHANGE); - compiler->addFunctionCall(&changeSizeBy); -} - -void LooksBlocks::compileSetSizeTo(Compiler *compiler) -{ - compiler->addInput(SIZE); - compiler->addFunctionCall(&setSizeTo); -} - -void LooksBlocks::compileSize(Compiler *compiler) -{ - compiler->addFunctionCall(&size); -} - -void LooksBlocks::compileSwitchCostumeTo(Compiler *compiler) -{ - Target *target = compiler->target(); - - if (!target) - return; - - compiler->addInput(COSTUME); - compiler->addFunctionCall(&switchCostumeTo); -} - -void LooksBlocks::compileNextCostume(Compiler *compiler) -{ - compiler->addFunctionCall(&nextCostume); -} - -void LooksBlocks::compileSwitchBackdropTo(Compiler *compiler) -{ - compiler->addInput(BACKDROP); - compiler->addFunctionCall(&switchBackdropTo); -} - -void LooksBlocks::compileSwitchBackdropToAndWait(Compiler *compiler) -{ - compiler->addInput(BACKDROP); - compiler->addFunctionCall(&switchBackdropToAndWait); -} - -void LooksBlocks::compileNextBackdrop(Compiler *compiler) -{ - compiler->addFunctionCall(&nextBackdrop); -} - -void LooksBlocks::compileGoToFrontBack(Compiler *compiler) -{ - int option = compiler->field(FRONT_BACK)->specialValueId(); - - switch (option) { - case Front: - compiler->addFunctionCall(&goToFront); - break; - - case Back: - compiler->addFunctionCall(&goToBack); - break; - } -} - -void LooksBlocks::compileGoForwardBackwardLayers(Compiler *compiler) -{ - int option = compiler->field(FORWARD_BACKWARD)->specialValueId(); - - switch (option) { - case Forward: - compiler->addInput(NUM); - compiler->addFunctionCall(&goForwardLayers); - break; - - case Backward: - compiler->addInput(NUM); - compiler->addFunctionCall(&goBackwardLayers); - break; - } -} - -void LooksBlocks::compileCostumeNumberName(Compiler *compiler) -{ - int option = compiler->field(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: - compiler->addFunctionCall(&costumeNumber); - break; - - case Name: - compiler->addFunctionCall(&costumeName); - break; - } -} - -void LooksBlocks::compileBackdropNumberName(Compiler *compiler) -{ - int option = compiler->field(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: - compiler->addFunctionCall(&backdropNumber); - break; - - case Name: - compiler->addFunctionCall(&backdropName); - break; - } -} - -const std::string &LooksBlocks::costumeNumberNameMonitorName(Block *block) -{ - int option = block->findFieldById(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: { - static const std::string name = "costume number"; - return name; - } - - case Name: { - static const std::string name = "costume name"; - return name; - } - - default: { - static const std::string name = ""; - return name; - } - } -} - -const std::string &LooksBlocks::backdropNumberNameMonitorName(Block *block) -{ - int option = block->findFieldById(NUMBER_NAME)->specialValueId(); - - switch (option) { - case Number: { - static const std::string name = "backdrop number"; - return name; - } - - case Name: { - static const std::string name = "backdrop name"; - return name; - } - - default: { - static const std::string name = ""; - return name; - } - } -} - -const std::string &LooksBlocks::sizeMonitorName(Block *block) -{ - static const std::string name = "size"; - return name; -} - -void LooksBlocks::startWait(VirtualMachine *vm, double secs) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - m_timeMap[vm] = { currentTime, secs * 1000 }; - vm->engine()->requestRedraw(); -} - -bool LooksBlocks::wait(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - assert(m_timeMap.count(vm) == 1); - - if (std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count() >= m_timeMap[vm].second) { - m_timeMap.erase(vm); - vm->stop(true, true, false); - return true; - } else { - vm->stop(true, true, true); - return false; - } -} - -void LooksBlocks::showBubble(VirtualMachine *vm, TextBubble::Type type, const std::string &text) -{ - Target *target = vm->target(); - - if (target) { - target->bubble()->setType(type); - target->bubble()->setText(text); - m_waitingBubbles.erase(target); - } -} - -void LooksBlocks::hideBubble(Target *target) -{ - if (!target) - return; - - target->bubble()->setText(""); - m_waitingBubbles.erase(target); -} - -unsigned int LooksBlocks::startSayForSecs(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) { - showBubble(vm, TextBubble::Type::Say, vm->getInput(0, 2)->toString()); - m_waitingBubbles[target] = vm; - startWait(vm, vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int LooksBlocks::sayForSecs(VirtualMachine *vm) -{ - if (wait(vm)) { - Target *target = vm->target(); - - if (target) { - auto it = m_waitingBubbles.find(target); - - // Clear bubble if it hasn't been changed - if (it != m_waitingBubbles.cend() && it->second == vm) - hideBubble(vm->target()); - } - } - - return 0; -} - -unsigned int LooksBlocks::say(VirtualMachine *vm) -{ - showBubble(vm, TextBubble::Type::Say, vm->getInput(0, 1)->toString()); - return 1; -} - -unsigned int LooksBlocks::startThinkForSecs(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) { - showBubble(vm, TextBubble::Type::Think, vm->getInput(0, 2)->toString()); - m_waitingBubbles[target] = vm; - startWait(vm, vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int LooksBlocks::thinkForSecs(VirtualMachine *vm) -{ - return sayForSecs(vm); // there isn't any difference -} - -unsigned int LooksBlocks::think(VirtualMachine *vm) -{ - showBubble(vm, TextBubble::Type::Think, vm->getInput(0, 1)->toString()); - return 1; -} - -unsigned int LooksBlocks::show(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setVisible(true); - - return 0; -} - -unsigned int LooksBlocks::hide(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setVisible(false); - - return 0; -} - -unsigned int LooksBlocks::changeEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) { - IGraphicsEffect *effect = m_customGraphicsEffects[vm->getInput(0, 2)->toLong()]; - target->setGraphicsEffectValue(effect, target->graphicsEffectValue(effect) + vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int LooksBlocks::changeColorEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_colorEffect, target->graphicsEffectValue(m_colorEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeFisheyeEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_fisheyeEffect, target->graphicsEffectValue(m_fisheyeEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeWhirlEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_whirlEffect, target->graphicsEffectValue(m_whirlEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changePixelateEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_pixelateEffect, target->graphicsEffectValue(m_pixelateEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeMosaicEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_mosaicEffect, target->graphicsEffectValue(m_mosaicEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeBrightnessEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_brightnessEffect, target->graphicsEffectValue(m_brightnessEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::changeGhostEffectBy(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_ghostEffect, target->graphicsEffectValue(m_ghostEffect) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_customGraphicsEffects[vm->getInput(0, 2)->toLong()], vm->getInput(1, 2)->toDouble()); - - return 2; -} - -unsigned int LooksBlocks::setColorEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_colorEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setFisheyeEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_fisheyeEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setWhirlEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_whirlEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setPixelateEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_pixelateEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setMosaicEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_mosaicEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setBrightnessEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_brightnessEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setGhostEffectTo(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->setGraphicsEffectValue(m_ghostEffect, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::clearGraphicEffects(VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (target) - target->clearGraphicsEffects(); - - return 0; -} - -unsigned int LooksBlocks::changeSizeBy(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setSize(sprite->size() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::setSizeTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setSize(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int LooksBlocks::size(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->size()); - else - vm->addReturnValue(0); - - return 0; -} - -void LooksBlocks::setCostumeByIndex(Target *target, long index) -{ - long costumeCount = target->costumes().size(); - - if (index < 0 || index >= costumeCount) { - if (index < 0) - index = std::fmod(costumeCount + std::fmod(index, -costumeCount), costumeCount); - else - index = std::fmod(index, costumeCount); - } - - target->setCostumeIndex(index); -} - -unsigned int LooksBlocks::switchCostumeTo(VirtualMachine *vm) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/blocks/scratch3_looks.js#L389-L413 - Target *target = vm->target(); - - if (!target) - return 1; - - const Value *name = vm->getInput(0, 1); - - if (!name->isString()) { - // Numbers should be treated as costume indices, always - if (name->isNaN() || name->isInfinity() || name->isNegativeInfinity()) - target->setCostumeIndex(0); - else - setCostumeByIndex(target, name->toLong() - 1); - } else { - // Strings should be treated as costume names, where possible - const int costumeIndex = target->findCostume(name->toString()); - std::string nameStr = name->toString(); - - auto it = std::find_if(nameStr.begin(), nameStr.end(), [](char c) { return !std::isspace(c); }); - bool isWhiteSpace = (it == nameStr.end()); - - if (costumeIndex != -1) { - setCostumeByIndex(target, costumeIndex); - } else if (nameStr == "next costume") { - nextCostume(vm); - } else if (nameStr == "previous costume") { - previousCostume(vm); - // Try to cast the string to a number (and treat it as a costume index) - // Pure whitespace should not be treated as a number - // Note: isNaN will cast the string to a number before checking if it's NaN - } else if (!(name->isNaN() || isWhiteSpace)) { - target->setCostumeIndex(name->toInt() - 1); - } - } - - return 1; -} - -unsigned int LooksBlocks::nextCostume(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - setCostumeByIndex(target, target->costumeIndex() + 1); - - return 0; -} - -unsigned int LooksBlocks::previousCostume(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - setCostumeByIndex(target, target->costumeIndex() - 1); - - return 0; -} - -void LooksBlocks::startBackdropScripts(VirtualMachine *vm, bool wait) -{ - if (Stage *stage = vm->engine()->stage()) { - if (stage->costumes().size() > 0) - vm->engine()->startBackdropScripts(stage->currentCostume()->broadcast(), vm->thread(), wait); - } -} - -void LooksBlocks::switchBackdropToImpl(VirtualMachine *vm) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/blocks/scratch3_looks.js#L423-L462 - Stage *stage = vm->engine()->stage(); - - if (!stage) - return; - - const Value *name = vm->getInput(0, 1); - - if (!name->isString()) { - // Numbers should be treated as costume indices, always - if (name->isNaN() || name->isInfinity() || name->isNegativeInfinity()) - stage->setCostumeIndex(0); - else - setCostumeByIndex(stage, name->toLong() - 1); - } else { - // Strings should be treated as costume names, where possible - const int costumeIndex = stage->findCostume(name->toString()); - std::string nameStr = name->toString(); - - auto it = std::find_if(nameStr.begin(), nameStr.end(), [](char c) { return !std::isspace(c); }); - bool isWhiteSpace = (it == nameStr.end()); - - if (costumeIndex != -1) { - setCostumeByIndex(stage, costumeIndex); - } else if (nameStr == "next backdrop") { - nextBackdropImpl(vm); - } else if (nameStr == "previous backdrop") { - previousBackdropImpl(vm); - } else if (nameStr == "random backdrop") { - randomBackdropImpl(vm); - // Try to cast the string to a number (and treat it as a costume index) - // Pure whitespace should not be treated as a number - // Note: isNaN will cast the string to a number before checking if it's NaN - } else if (!(name->isNaN() || isWhiteSpace)) { - stage->setCostumeIndex(name->toInt() - 1); - } - } -} - -void LooksBlocks::nextBackdropImpl(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) - setCostumeByIndex(stage, stage->costumeIndex() + 1); -} - -void LooksBlocks::previousBackdropImpl(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) - setCostumeByIndex(stage, stage->costumeIndex() - 1); -} - -void LooksBlocks::randomBackdropImpl(VirtualMachine *vm) -{ - if (!rng) - rng = RandomGenerator::instance().get(); - - if (Stage *stage = vm->engine()->stage()) { - std::size_t count = stage->costumes().size(); - - if (count > 1) { - stage->setCostumeIndex(rng->randintExcept(0, count - 1, stage->costumeIndex())); - } - } -} - -unsigned int LooksBlocks::switchBackdropTo(VirtualMachine *vm) -{ - switchBackdropToImpl(vm); - startBackdropScripts(vm, false); - - return 1; -} - -unsigned int LooksBlocks::switchBackdropToAndWait(VirtualMachine *vm) -{ - switchBackdropToImpl(vm); - startBackdropScripts(vm, true); - vm->promise(); - - return 1; -} - -unsigned int LooksBlocks::nextBackdrop(VirtualMachine *vm) -{ - nextBackdropImpl(vm); - startBackdropScripts(vm, false); - - return 0; -} - -unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm) -{ - previousBackdropImpl(vm); - startBackdropScripts(vm, false); - - return 0; -} - -unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm) -{ - randomBackdropImpl(vm); - startBackdropScripts(vm, false); - - return 0; -} - -unsigned int LooksBlocks::goToFront(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableToFront(sprite); - - return 0; -} - -unsigned int LooksBlocks::goToBack(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableToBack(sprite); - - return 0; -} - -unsigned int LooksBlocks::goForwardLayers(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableForwardLayers(sprite, vm->getInput(0, 1)->toInt()); - - return 1; -} - -unsigned int LooksBlocks::goBackwardLayers(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->engine()->moveDrawableBackwardLayers(sprite, vm->getInput(0, 1)->toInt()); - - return 1; -} - -unsigned int LooksBlocks::costumeNumber(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - vm->addReturnValue(target->costumeIndex() + 1); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int LooksBlocks::costumeName(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - auto costume = target->currentCostume(); - - if (costume) - vm->addReturnValue(costume->name()); - else - vm->addReturnValue(""); - } else - vm->addReturnValue(""); - - return 0; -} - -unsigned int LooksBlocks::backdropNumber(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) - vm->addReturnValue(stage->costumeIndex() + 1); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int LooksBlocks::backdropName(VirtualMachine *vm) -{ - if (Stage *stage = vm->engine()->stage()) { - auto costume = stage->currentCostume(); - - if (costume) - vm->addReturnValue(costume->name()); - else - vm->addReturnValue(""); - } else - vm->addReturnValue(""); - - return 0; -} diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h deleted file mode 100644 index 8881d7eb..00000000 --- a/src/blocks/looksblocks.h +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace libscratchcpp -{ - -class Stage; -class Value; -class IGraphicsEffect; -class IRandomGenerator; -class IClock; - -class LooksBlocks : public IExtension -{ - public: - enum Inputs - { - MESSAGE, - SECS, - CHANGE, - SIZE, - COSTUME, - BACKDROP, - VALUE, - NUM - }; - - enum Fields - { - NUMBER_NAME, - EFFECT, - FRONT_BACK, - FORWARD_BACKWARD - }; - - enum FieldValues - { - Number, - Name, - ColorEffect, - FisheyeEffect, - WhirlEffect, - PixelateEffect, - MosaicEffect, - BrightnessEffect, - GhostEffect, - Front, - Back, - Forward, - Backward - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static void compileSayForSecs(Compiler *compiler); - static void compileSay(Compiler *compiler); - static void compileThinkForSecs(Compiler *compiler); - static void compileThink(Compiler *compiler); - static void compileShow(Compiler *compiler); - static void compileHide(Compiler *compiler); - static void compileChangeEffectBy(Compiler *compiler); - static void compileSetEffectTo(Compiler *compiler); - static void compileClearGraphicEffects(Compiler *compiler); - static void compileChangeSizeBy(Compiler *compiler); - static void compileSetSizeTo(Compiler *compiler); - static void compileSize(Compiler *compiler); - static void compileSwitchCostumeTo(Compiler *compiler); - static void compileNextCostume(Compiler *compiler); - static void compileSwitchBackdropTo(Compiler *compiler); - static void compileSwitchBackdropToAndWait(Compiler *compiler); - static void compileNextBackdrop(Compiler *compiler); - static void compileGoToFrontBack(Compiler *compiler); - static void compileGoForwardBackwardLayers(Compiler *compiler); - static void compileCostumeNumberName(Compiler *compiler); - static void compileBackdropNumberName(Compiler *compiler); - - static const std::string &costumeNumberNameMonitorName(Block *block); - static const std::string &backdropNumberNameMonitorName(Block *block); - static const std::string &sizeMonitorName(Block *block); - - static void startWait(VirtualMachine *vm, double secs); - static bool wait(VirtualMachine *vm); - static void showBubble(VirtualMachine *vm, TextBubble::Type type, const std::string &text); - static void hideBubble(Target *target); - - static unsigned int startSayForSecs(VirtualMachine *vm); - static unsigned int sayForSecs(VirtualMachine *vm); - static unsigned int say(VirtualMachine *vm); - - static unsigned int startThinkForSecs(VirtualMachine *vm); - static unsigned int thinkForSecs(VirtualMachine *vm); - static unsigned int think(VirtualMachine *vm); - - static unsigned int show(VirtualMachine *vm); - static unsigned int hide(VirtualMachine *vm); - - static unsigned int changeEffectBy(VirtualMachine *vm); - static unsigned int changeColorEffectBy(VirtualMachine *vm); - static unsigned int changeFisheyeEffectBy(VirtualMachine *vm); - static unsigned int changeWhirlEffectBy(VirtualMachine *vm); - static unsigned int changePixelateEffectBy(VirtualMachine *vm); - static unsigned int changeMosaicEffectBy(VirtualMachine *vm); - static unsigned int changeBrightnessEffectBy(VirtualMachine *vm); - static unsigned int changeGhostEffectBy(VirtualMachine *vm); - - static unsigned int setEffectTo(VirtualMachine *vm); - static unsigned int setColorEffectTo(VirtualMachine *vm); - static unsigned int setFisheyeEffectTo(VirtualMachine *vm); - static unsigned int setWhirlEffectTo(VirtualMachine *vm); - static unsigned int setPixelateEffectTo(VirtualMachine *vm); - static unsigned int setMosaicEffectTo(VirtualMachine *vm); - static unsigned int setBrightnessEffectTo(VirtualMachine *vm); - static unsigned int setGhostEffectTo(VirtualMachine *vm); - - static unsigned int clearGraphicEffects(VirtualMachine *vm); - static unsigned int changeSizeBy(VirtualMachine *vm); - static unsigned int setSizeTo(VirtualMachine *vm); - static unsigned int size(VirtualMachine *vm); - - static void setCostumeByIndex(Target *target, long index); - static unsigned int switchCostumeTo(VirtualMachine *vm); - static unsigned int nextCostume(VirtualMachine *vm); - static unsigned int previousCostume(VirtualMachine *vm); - - static void startBackdropScripts(VirtualMachine *vm, bool wait); - static void switchBackdropToImpl(VirtualMachine *vm); - static void nextBackdropImpl(VirtualMachine *vm); - static void previousBackdropImpl(VirtualMachine *vm); - static void randomBackdropImpl(VirtualMachine *vm); - - static unsigned int switchBackdropTo(VirtualMachine *vm); - static unsigned int switchBackdropToAndWait(VirtualMachine *vm); - static unsigned int nextBackdrop(VirtualMachine *vm); - static unsigned int previousBackdrop(VirtualMachine *vm); - static unsigned int randomBackdrop(VirtualMachine *vm); - - static unsigned int goToFront(VirtualMachine *vm); - static unsigned int goToBack(VirtualMachine *vm); - - static unsigned int goForwardLayers(VirtualMachine *vm); - static unsigned int goBackwardLayers(VirtualMachine *vm); - - static unsigned int costumeNumber(VirtualMachine *vm); - static unsigned int costumeName(VirtualMachine *vm); - static unsigned int backdropNumber(VirtualMachine *vm); - static unsigned int backdropName(VirtualMachine *vm); - - static inline std::unordered_map> m_timeMap; - static inline std::unordered_map m_waitingBubbles; - - static inline std::vector m_customGraphicsEffects; - static inline IGraphicsEffect *m_colorEffect = nullptr; - static inline IGraphicsEffect *m_fisheyeEffect = nullptr; - static inline IGraphicsEffect *m_whirlEffect = nullptr; - static inline IGraphicsEffect *m_pixelateEffect = nullptr; - static inline IGraphicsEffect *m_mosaicEffect = nullptr; - static inline IGraphicsEffect *m_brightnessEffect = nullptr; - static inline IGraphicsEffect *m_ghostEffect = nullptr; - - static IRandomGenerator *rng; - static IClock *clock; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/motionblocks.cpp b/src/blocks/motionblocks.cpp deleted file mode 100644 index c5a96004..00000000 --- a/src/blocks/motionblocks.cpp +++ /dev/null @@ -1,817 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include - -#include "motionblocks.h" -#include "../engine/internal/randomgenerator.h" -#include "../engine/internal/clock.h" - -using namespace libscratchcpp; - -static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20 - -IRandomGenerator *MotionBlocks::rng = nullptr; -IClock *MotionBlocks::clock = nullptr; - -std::string MotionBlocks::name() const -{ - return "Motion"; -} - -std::string MotionBlocks::description() const -{ - return name() + " blocks"; -} - -void MotionBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "motion_movesteps", &compileMoveSteps); - engine->addCompileFunction(this, "motion_turnright", &compileTurnRight); - engine->addCompileFunction(this, "motion_turnleft", &compileTurnLeft); - engine->addCompileFunction(this, "motion_pointindirection", &compilePointInDirection); - engine->addCompileFunction(this, "motion_pointtowards", &compilePointTowards); - engine->addCompileFunction(this, "motion_gotoxy", &compileGoToXY); - engine->addCompileFunction(this, "motion_goto", &compileGoTo); - engine->addCompileFunction(this, "motion_glidesecstoxy", &compileGlideSecsToXY); - engine->addCompileFunction(this, "motion_glideto", &compileGlideTo); - engine->addCompileFunction(this, "motion_changexby", &compileChangeXBy); - engine->addCompileFunction(this, "motion_setx", &compileSetX); - engine->addCompileFunction(this, "motion_changeyby", &compileChangeYBy); - engine->addCompileFunction(this, "motion_sety", &compileSetY); - engine->addCompileFunction(this, "motion_ifonedgebounce", &compileIfOnEdgeBounce); - engine->addCompileFunction(this, "motion_setrotationstyle", &compileSetRotationStyle); - engine->addCompileFunction(this, "motion_xposition", &compileXPosition); - engine->addCompileFunction(this, "motion_yposition", &compileYPosition); - engine->addCompileFunction(this, "motion_direction", &compileDirection); - - // Monitor names - engine->addMonitorNameFunction(this, "motion_xposition", &xPositionMonitorName); - engine->addMonitorNameFunction(this, "motion_yposition", &yPositionMonitorName); - engine->addMonitorNameFunction(this, "motion_direction", &directionMonitorName); - - // Inputs - engine->addInput(this, "STEPS", STEPS); - engine->addInput(this, "DEGREES", DEGREES); - engine->addInput(this, "DIRECTION", DIRECTION); - engine->addInput(this, "TOWARDS", TOWARDS); - engine->addInput(this, "X", X); - engine->addInput(this, "Y", Y); - engine->addInput(this, "TO", TO); - engine->addInput(this, "SECS", SECS); - engine->addInput(this, "DX", DX); - engine->addInput(this, "DY", DY); - - // Fields - engine->addField(this, "STYLE", STYLE); - - // Field values - engine->addFieldValue(this, "left-right", LeftRight); - engine->addFieldValue(this, "don't rotate", DoNotRotate); - engine->addFieldValue(this, "all around", AllAround); -} - -void MotionBlocks::onInit(IEngine *engine) -{ - engine->threadAboutToStop().connect([](Thread *thread) { - m_timeMap.erase(thread->vm()); - m_glideMap.erase(thread->vm()); - }); -} - -void MotionBlocks::compileMoveSteps(Compiler *compiler) -{ - compiler->addInput(STEPS); - compiler->addFunctionCall(&moveSteps); -} - -void MotionBlocks::compileTurnRight(Compiler *compiler) -{ - compiler->addInput(DEGREES); - compiler->addFunctionCall(&turnRight); -} - -void MotionBlocks::compileTurnLeft(Compiler *compiler) -{ - compiler->addInput(DEGREES); - compiler->addFunctionCall(&turnLeft); -} - -void MotionBlocks::compilePointInDirection(Compiler *compiler) -{ - compiler->addInput(DIRECTION); - compiler->addFunctionCall(&pointInDirection); -} - -void MotionBlocks::compilePointTowards(Compiler *compiler) -{ - Input *input = compiler->input(TOWARDS); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&pointTowardsMousePointer); - else if (value == "_random_") - compiler->addFunctionCall(&pointTowardsRandomPosition); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&pointTowardsByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&pointTowards); - } -} - -void MotionBlocks::compileGoToXY(Compiler *compiler) -{ - compiler->addInput(X); - compiler->addInput(Y); - compiler->addFunctionCall(&goToXY); -} - -void MotionBlocks::compileGoTo(Compiler *compiler) -{ - Input *input = compiler->input(TO); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&goToMousePointer); - else if (value == "_random_") - compiler->addFunctionCall(&goToRandomPosition); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&goToByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&goTo); - } -} - -void MotionBlocks::compileGlideSecsToXY(Compiler *compiler) -{ - compiler->addInput(SECS); - compiler->addInput(X); - compiler->addInput(Y); - compiler->addFunctionCall(&startGlideSecsTo); - compiler->addFunctionCall(&glideSecsTo); -} - -void MotionBlocks::compileGlideTo(Compiler *compiler) -{ - compiler->addInput(SECS); - - Input *input = compiler->input(TO); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&startGlideToMousePointer); - else if (value == "_random_") - compiler->addFunctionCall(&startGlideToRandomPosition); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&startGlideToByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&startGlideTo); - } - - compiler->addFunctionCall(&glideSecsTo); -} - -void MotionBlocks::compileChangeXBy(Compiler *compiler) -{ - compiler->addInput(DX); - compiler->addFunctionCall(&changeXBy); -} - -void MotionBlocks::compileSetX(Compiler *compiler) -{ - compiler->addInput(X); - compiler->addFunctionCall(&setX); -} - -void MotionBlocks::compileChangeYBy(Compiler *compiler) -{ - compiler->addInput(DY); - compiler->addFunctionCall(&changeYBy); -} - -void MotionBlocks::compileSetY(Compiler *compiler) -{ - compiler->addInput(Y); - compiler->addFunctionCall(&setY); -} - -void MotionBlocks::compileIfOnEdgeBounce(Compiler *compiler) -{ - compiler->addFunctionCall(&ifOnEdgeBounce); -} - -void MotionBlocks::compileSetRotationStyle(Compiler *compiler) -{ - int option = compiler->field(STYLE)->specialValueId(); - - switch (option) { - case LeftRight: - compiler->addFunctionCall(&setLeftRightRotationStyle); - break; - - case DoNotRotate: - compiler->addFunctionCall(&setDoNotRotateRotationStyle); - break; - - case AllAround: - compiler->addFunctionCall(&setAllAroundRotationStyle); - break; - - default: - break; - } -} - -void MotionBlocks::compileXPosition(Compiler *compiler) -{ - compiler->addFunctionCall(&xPosition); -} - -void MotionBlocks::compileYPosition(Compiler *compiler) -{ - compiler->addFunctionCall(&yPosition); -} - -void MotionBlocks::compileDirection(Compiler *compiler) -{ - compiler->addFunctionCall(&direction); -} - -const std::string &MotionBlocks::xPositionMonitorName(Block *block) -{ - static const std::string name = "x position"; - return name; -} - -const std::string &MotionBlocks::yPositionMonitorName(Block *block) -{ - static const std::string name = "y position"; - return name; -} - -const std::string &MotionBlocks::directionMonitorName(Block *block) -{ - static const std::string name = "direction"; - return name; -} - -unsigned int MotionBlocks::moveSteps(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - double dir = sprite->direction(); - double steps = vm->getInput(0, 1)->toDouble(); - sprite->setPosition(sprite->x() + std::sin(dir * pi / 180) * steps, sprite->y() + std::cos(dir * pi / 180) * steps); - } - - return 1; -} - -unsigned int MotionBlocks::turnRight(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setDirection(sprite->direction() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::turnLeft(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setDirection(sprite->direction() - vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::pointInDirection(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setDirection(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -void MotionBlocks::pointTowardsPos(Sprite *sprite, double x, double y) -{ - if (!sprite) - return; - - // https://en.scratch-wiki.info/wiki/Point_Towards_()_(block)#Workaround - double deltaX = x - sprite->x(); - double deltaY = y - sprite->y(); - - if (deltaY == 0) { - if (deltaX < 0) - sprite->setDirection(-90); - else - sprite->setDirection(90); - } else if (deltaY < 0) - sprite->setDirection(180 + (180 / pi) * std::atan(deltaX / deltaY)); - else - sprite->setDirection((180 / pi) * std::atan(deltaX / deltaY)); -} - -unsigned int MotionBlocks::pointTowards(VirtualMachine *vm) -{ - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - pointTowardsPos(dynamic_cast(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY()); - else if (value == "_random_") { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - pointTowardsPos(dynamic_cast(vm->target()), rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - } else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - pointTowardsPos(dynamic_cast(vm->target()), sprite->x(), sprite->y()); - } - - return 1; -} - -unsigned int MotionBlocks::pointTowardsByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - pointTowardsPos(dynamic_cast(vm->target()), sprite->x(), sprite->y()); - - return 1; -} - -unsigned int MotionBlocks::pointTowardsMousePointer(VirtualMachine *vm) -{ - pointTowardsPos(dynamic_cast(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY()); - return 0; -} - -unsigned int MotionBlocks::pointTowardsRandomPosition(VirtualMachine *vm) -{ - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - pointTowardsPos(dynamic_cast(vm->target()), rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - - return 0; -} - -unsigned int MotionBlocks::goToXY(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - sprite->setPosition(vm->getInput(0, 2)->toDouble(), vm->getInput(1, 2)->toDouble()); - } - - return 2; -} - -unsigned int MotionBlocks::goTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) - return 1; - - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - sprite->setPosition(vm->engine()->mouseX(), vm->engine()->mouseY()); - else if (value == "_random_") { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - sprite->setPosition(rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - } else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *targetSprite = dynamic_cast(target); - - if (targetSprite) - sprite->setPosition(targetSprite->x(), targetSprite->y()); - } - - return 1; -} - -unsigned int MotionBlocks::goToByIndex(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *targetSprite = dynamic_cast(target); - - if (sprite && targetSprite) - sprite->setPosition(targetSprite->x(), targetSprite->y()); - - return 1; -} - -unsigned int MotionBlocks::goToMousePointer(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setPosition(vm->engine()->mouseX(), vm->engine()->mouseY()); - - return 0; -} - -unsigned int MotionBlocks::goToRandomPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - sprite->setPosition(rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2)); - } - - return 0; -} - -void MotionBlocks::startGlidingToPos(VirtualMachine *vm, double x, double y, double secs) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) - return; - - if (secs <= 0) { - if (sprite) - sprite->setPosition(x, y); - - return; - } - - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - m_timeMap[vm] = { currentTime, secs * 1000 }; - m_glideMap[vm] = { { sprite->x(), sprite->y() }, { x, y } }; -} - -void MotionBlocks::continueGliding(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto currentTime = clock->currentSteadyTime(); - auto elapsedTime = std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count(); - auto maxTime = m_timeMap[vm].second; - assert(m_timeMap.count(vm) == 1); - - Sprite *sprite = dynamic_cast(vm->target()); - double x = m_glideMap[vm].second.first; - double y = m_glideMap[vm].second.second; - - if (elapsedTime >= maxTime) { - if (sprite) - sprite->setPosition(x, y); - - m_timeMap.erase(vm); - m_glideMap.erase(vm); - } else { - if (sprite) { - double startX = m_glideMap[vm].first.first; - double startY = m_glideMap[vm].first.second; - double factor = elapsedTime / static_cast(maxTime); - assert(factor >= 0 && factor < 1); - - sprite->setPosition(startX + (x - startX) * factor, startY + (y - startY) * factor); - } - - vm->stop(true, true, true); - } -} - -unsigned int MotionBlocks::startGlideSecsTo(VirtualMachine *vm) -{ - startGlidingToPos(vm, vm->getInput(1, 3)->toDouble(), vm->getInput(2, 3)->toDouble(), vm->getInput(0, 3)->toDouble()); - return 3; -} - -unsigned int MotionBlocks::glideSecsTo(VirtualMachine *vm) -{ - if (m_timeMap.find(vm) != m_timeMap.cend()) { - assert(m_glideMap.find(vm) != m_glideMap.cend()); - continueGliding(vm); - } - - return 0; -} - -unsigned int MotionBlocks::startGlideTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) - return 1; - - std::string value = vm->getInput(1, 2)->toString(); - - if (value == "_mouse_") - startGlidingToPos(vm, vm->engine()->mouseX(), vm->engine()->mouseY(), vm->getInput(0, 2)->toDouble()); - else if (value == "_random_") { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - startGlidingToPos(vm, rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2), vm->getInput(0, 2)->toDouble()); - } else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *targetSprite = dynamic_cast(target); - - if (targetSprite) - startGlidingToPos(vm, targetSprite->x(), targetSprite->y(), vm->getInput(0, 2)->toDouble()); - } - - return 2; -} - -unsigned int MotionBlocks::startGlideToByIndex(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - Target *target = vm->engine()->targetAt(vm->getInput(1, 2)->toInt()); - Sprite *targetSprite = dynamic_cast(target); - - if (sprite && targetSprite) - startGlidingToPos(vm, targetSprite->x(), targetSprite->y(), vm->getInput(0, 2)->toDouble()); - - return 2; -} - -unsigned int MotionBlocks::startGlideToMousePointer(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - startGlidingToPos(vm, vm->engine()->mouseX(), vm->engine()->mouseY(), vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::startGlideToRandomPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) { - const unsigned int stageWidth = vm->engine()->stageWidth(); - const unsigned int stageHeight = vm->engine()->stageHeight(); - - if (!rng) - rng = RandomGenerator::instance().get(); - - startGlidingToPos(vm, rng->randint(-static_cast(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast(stageHeight / 2), stageHeight / 2), vm->getInput(0, 1)->toDouble()); - } - - return 1; -} - -unsigned int MotionBlocks::changeXBy(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setX(sprite->x() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::setX(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setX(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::changeYBy(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setY(sprite->y() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::setY(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setY(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int MotionBlocks::ifOnEdgeBounce(VirtualMachine *vm) -{ - // See https://github.com/scratchfoundation/scratch-vm/blob/c37745e97e6d8a77ad1dc31a943ea728dd17ba78/src/blocks/scratch3_motion.js#L186-L240 - Sprite *sprite = dynamic_cast(vm->target()); - IEngine *engine = vm->engine(); - - if (!sprite || !engine) - return 0; - - Rect bounds = sprite->boundingRect(); - - // Measure distance to edges - // Values are zero when the sprite is beyond - double stageWidth = engine->stageWidth(); - double stageHeight = engine->stageHeight(); - double distLeft = std::max(0.0, (stageWidth / 2.0) + bounds.left()); - double distTop = std::max(0.0, (stageHeight / 2.0) - bounds.top()); - double distRight = std::max(0.0, (stageWidth / 2.0) - bounds.right()); - double distBottom = std::max(0.0, (stageHeight / 2.0) + bounds.bottom()); - - // Find the nearest edge - // 1 - left - // 2 - top - // 3 - right - // 4 - bottom - unsigned short nearestEdge = 0; - double minDist = std::numeric_limits::infinity(); - - if (distLeft < minDist) { - minDist = distLeft; - nearestEdge = 1; - } - - if (distTop < minDist) { - minDist = distTop; - nearestEdge = 2; - } - - if (distRight < minDist) { - minDist = distRight; - nearestEdge = 3; - } - - if (distBottom < minDist) { - minDist = distBottom; - nearestEdge = 4; - } - - if (minDist > 0) { - return 0; // Not touching any edge - } - - assert(nearestEdge != 0); - - // Point away from the nearest edge - double radians = (90 - sprite->direction()) * pi / 180; - double dx = std::cos(radians); - double dy = -std::sin(radians); - - switch (nearestEdge) { - case 1: - // Left - dx = std::max(0.2, std::abs(dx)); - break; - - case 2: - // Top - dy = std::max(0.2, std::abs(dy)); - break; - - case 3: - // Right - dx = 0 - std::max(0.2, std::abs(dx)); - break; - - case 4: - // Bottom - dy = 0 - std::max(0.2, std::abs(dy)); - break; - } - - double newDirection = (180 / pi) * (std::atan2(dy, dx)) + 90; - sprite->setDirection(newDirection); - - // Keep within the stage - double fencedX, fencedY; - sprite->keepInFence(sprite->x(), sprite->y(), &fencedX, &fencedY); - sprite->setPosition(fencedX, fencedY); - - return 0; -} - -unsigned int MotionBlocks::setLeftRightRotationStyle(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setRotationStyle(Sprite::RotationStyle::LeftRight); - - return 0; -} - -unsigned int MotionBlocks::setDoNotRotateRotationStyle(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setRotationStyle(Sprite::RotationStyle::DoNotRotate); - - return 0; -} - -unsigned int MotionBlocks::setAllAroundRotationStyle(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - sprite->setRotationStyle(Sprite::RotationStyle::AllAround); - - return 0; -} - -unsigned int MotionBlocks::xPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->x()); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int MotionBlocks::yPosition(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->y()); - else - vm->addReturnValue(0); - - return 0; -} - -unsigned int MotionBlocks::direction(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(sprite->direction()); - else - vm->addReturnValue(0); - - return 0; -} diff --git a/src/blocks/motionblocks.h b/src/blocks/motionblocks.h deleted file mode 100644 index b545822e..00000000 --- a/src/blocks/motionblocks.h +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include - -namespace libscratchcpp -{ - -class Sprite; -class IRandomGenerator; -class IClock; - -class MotionBlocks : public IExtension -{ - public: - enum Inputs - { - STEPS, - DEGREES, - DIRECTION, - TOWARDS, - X, - Y, - TO, - SECS, - DX, - DY - }; - - enum Fields - { - STYLE - }; - - enum FieldValues - { - LeftRight, - DoNotRotate, - AllAround - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static void compileMoveSteps(Compiler *compiler); - static void compileTurnRight(Compiler *compiler); - static void compileTurnLeft(Compiler *compiler); - static void compilePointInDirection(Compiler *compiler); - static void compilePointTowards(Compiler *compiler); - static void compileGoToXY(Compiler *compiler); - static void compileGoTo(Compiler *compiler); - static void compileGlideSecsToXY(Compiler *compiler); - static void compileGlideTo(Compiler *compiler); - static void compileChangeXBy(Compiler *compiler); - static void compileSetX(Compiler *compiler); - static void compileChangeYBy(Compiler *compiler); - static void compileSetY(Compiler *compiler); - static void compileIfOnEdgeBounce(Compiler *compiler); - static void compileSetRotationStyle(Compiler *compiler); - static void compileXPosition(Compiler *compiler); - static void compileYPosition(Compiler *compiler); - static void compileDirection(Compiler *compiler); - - static const std::string &xPositionMonitorName(Block *block); - static const std::string &yPositionMonitorName(Block *block); - static const std::string &directionMonitorName(Block *block); - - static unsigned int moveSteps(VirtualMachine *vm); - static unsigned int turnRight(VirtualMachine *vm); - static unsigned int turnLeft(VirtualMachine *vm); - static unsigned int pointInDirection(VirtualMachine *vm); - - static void pointTowardsPos(Sprite *sprite, double x, double y); - - static unsigned int pointTowards(VirtualMachine *vm); - static unsigned int pointTowardsByIndex(VirtualMachine *vm); - static unsigned int pointTowardsMousePointer(VirtualMachine *vm); - static unsigned int pointTowardsRandomPosition(VirtualMachine *vm); - - static unsigned int goToXY(VirtualMachine *vm); - static unsigned int goTo(VirtualMachine *vm); - static unsigned int goToByIndex(VirtualMachine *vm); - static unsigned int goToMousePointer(VirtualMachine *vm); - static unsigned int goToRandomPosition(VirtualMachine *vm); - - static void startGlidingToPos(VirtualMachine *vm, double x, double y, double secs); - static void continueGliding(VirtualMachine *vm); - - static unsigned int startGlideSecsTo(VirtualMachine *vm); - static unsigned int glideSecsTo(VirtualMachine *vm); - static unsigned int startGlideTo(VirtualMachine *vm); - static unsigned int startGlideToByIndex(VirtualMachine *vm); - static unsigned int startGlideToMousePointer(VirtualMachine *vm); - static unsigned int startGlideToRandomPosition(VirtualMachine *vm); - - static unsigned int changeXBy(VirtualMachine *vm); - static unsigned int setX(VirtualMachine *vm); - static unsigned int changeYBy(VirtualMachine *vm); - static unsigned int setY(VirtualMachine *vm); - - static unsigned int ifOnEdgeBounce(VirtualMachine *vm); - - static unsigned int setLeftRightRotationStyle(VirtualMachine *vm); - static unsigned int setDoNotRotateRotationStyle(VirtualMachine *vm); - static unsigned int setAllAroundRotationStyle(VirtualMachine *vm); - - static unsigned int xPosition(VirtualMachine *vm); - static unsigned int yPosition(VirtualMachine *vm); - static unsigned int direction(VirtualMachine *vm); - - static IRandomGenerator *rng; - static IClock *clock; - - static inline std::unordered_map> m_timeMap; - static inline std::unordered_map, std::pair>> m_glideMap; // start pos, end pos -}; - -} // namespace libscratchcpp diff --git a/src/blocks/operatorblocks.cpp b/src/blocks/operatorblocks.cpp deleted file mode 100644 index 376ae27a..00000000 --- a/src/blocks/operatorblocks.cpp +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include - -#include "operatorblocks.h" - -using namespace libscratchcpp; - -std::string OperatorBlocks::name() const -{ - return "Operators"; -} - -std::string OperatorBlocks::description() const -{ - return "Operator blocks"; -} - -void OperatorBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "operator_add", &compileAdd); - engine->addCompileFunction(this, "operator_subtract", &compileSubtract); - engine->addCompileFunction(this, "operator_multiply", &compileMultiply); - engine->addCompileFunction(this, "operator_divide", &compileDivide); - engine->addCompileFunction(this, "operator_random", &compilePickRandom); - engine->addCompileFunction(this, "operator_lt", &compileLessThan); - engine->addCompileFunction(this, "operator_equals", &compileEquals); - engine->addCompileFunction(this, "operator_gt", &compileGreaterThan); - engine->addCompileFunction(this, "operator_and", &compileAnd); - engine->addCompileFunction(this, "operator_or", &compileOr); - engine->addCompileFunction(this, "operator_not", &compileNot); - engine->addCompileFunction(this, "operator_join", &compileJoin); - engine->addCompileFunction(this, "operator_letter_of", &compileLetterOf); - engine->addCompileFunction(this, "operator_length", &compileLength); - engine->addCompileFunction(this, "operator_contains", &compileContains); - engine->addCompileFunction(this, "operator_mod", &compileMod); - engine->addCompileFunction(this, "operator_round", &compileRound); - engine->addCompileFunction(this, "operator_mathop", &compileMathOp); - - // Inputs - engine->addInput(this, "NUM1", NUM1); - engine->addInput(this, "NUM2", NUM2); - engine->addInput(this, "FROM", FROM); - engine->addInput(this, "TO", TO); - engine->addInput(this, "OPERAND1", OPERAND1); - engine->addInput(this, "OPERAND2", OPERAND2); - engine->addInput(this, "OPERAND", OPERAND); - engine->addInput(this, "STRING1", STRING1); - engine->addInput(this, "STRING2", STRING2); - engine->addInput(this, "LETTER", LETTER); - engine->addInput(this, "STRING", STRING); - engine->addInput(this, "NUM", NUM); - - // Fields - engine->addField(this, "OPERATOR", OPERATOR); - - // Field values - engine->addFieldValue(this, "abs", Abs); - engine->addFieldValue(this, "floor", Floor); - engine->addFieldValue(this, "ceiling", Ceiling); - engine->addFieldValue(this, "sqrt", Sqrt); - engine->addFieldValue(this, "sin", Sin); - engine->addFieldValue(this, "cos", Cos); - engine->addFieldValue(this, "tan", Tan); - engine->addFieldValue(this, "asin", Asin); - engine->addFieldValue(this, "acos", Acos); - engine->addFieldValue(this, "atan", Atan); - engine->addFieldValue(this, "ln", Ln); - engine->addFieldValue(this, "log", Log); - engine->addFieldValue(this, "e ^", Eexp); - engine->addFieldValue(this, "10 ^", Op_10exp); -} - -void OperatorBlocks::compileAdd(Compiler *compiler) -{ - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_ADD); -} - -void OperatorBlocks::compileSubtract(Compiler *compiler) -{ - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_SUBTRACT); -} - -void OperatorBlocks::compileMultiply(Compiler *compiler) -{ - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_MULTIPLY); -} - -void OperatorBlocks::compileDivide(Compiler *compiler) -{ - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_DIVIDE); -} - -void OperatorBlocks::compilePickRandom(Compiler *compiler) -{ - compiler->addInput(FROM); - compiler->addInput(TO); - compiler->addInstruction(vm::OP_RANDOM); -} - -void OperatorBlocks::compileLessThan(Compiler *compiler) -{ - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_LESS_THAN); -} - -void OperatorBlocks::compileEquals(Compiler *compiler) -{ - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_EQUALS); -} - -void OperatorBlocks::compileGreaterThan(Compiler *compiler) -{ - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_GREATER_THAN); -} - -void OperatorBlocks::compileAnd(Compiler *compiler) -{ - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_AND); -} - -void OperatorBlocks::compileOr(Compiler *compiler) -{ - compiler->addInput(OPERAND1); - compiler->addInput(OPERAND2); - compiler->addInstruction(vm::OP_OR); -} - -void OperatorBlocks::compileNot(Compiler *compiler) -{ - compiler->addInput(OPERAND); - compiler->addInstruction(vm::OP_NOT); -} - -void OperatorBlocks::compileJoin(Compiler *compiler) -{ - compiler->addInput(STRING1); - compiler->addInput(STRING2); - compiler->addInstruction(vm::OP_STR_CONCAT); -} - -void OperatorBlocks::compileLetterOf(Compiler *compiler) -{ - compiler->addInput(STRING); - compiler->addInput(LETTER); - compiler->addInstruction(vm::OP_STR_AT); -} - -void OperatorBlocks::compileLength(Compiler *compiler) -{ - compiler->addInput(STRING); - compiler->addInstruction(vm::OP_STR_LENGTH); -} - -void OperatorBlocks::compileContains(Compiler *compiler) -{ - compiler->addInput(STRING1); - compiler->addInput(STRING2); - compiler->addInstruction(vm::OP_STR_CONTAINS); -} - -void OperatorBlocks::compileMod(Compiler *compiler) -{ - compiler->addInput(NUM1); - compiler->addInput(NUM2); - compiler->addInstruction(vm::OP_MOD); -} - -void OperatorBlocks::compileRound(Compiler *compiler) -{ - compiler->addInput(NUM); - compiler->addInstruction(vm::OP_ROUND); -} - -void OperatorBlocks::compileMathOp(Compiler *compiler) -{ - compiler->addInput(NUM); - - int id = compiler->field(OPERATOR)->specialValueId(); - switch (id) { - case Abs: - compiler->addInstruction(vm::OP_ABS); - break; - - case Floor: - compiler->addInstruction(vm::OP_FLOOR); - break; - - case Ceiling: - compiler->addInstruction(vm::OP_CEIL); - break; - - case Sqrt: - compiler->addInstruction(vm::OP_SQRT); - break; - - case Sin: - compiler->addInstruction(vm::OP_SIN); - break; - - case Cos: - compiler->addInstruction(vm::OP_COS); - break; - - case Tan: - compiler->addInstruction(vm::OP_TAN); - break; - - case Asin: - compiler->addInstruction(vm::OP_ASIN); - break; - - case Acos: - compiler->addInstruction(vm::OP_ACOS); - break; - - case Atan: - compiler->addInstruction(vm::OP_ATAN); - break; - - case Ln: - compiler->addFunctionCall(&op_ln); - break; - - case Log: - compiler->addFunctionCall(&op_log); - break; - - case Eexp: - compiler->addFunctionCall(&op_eexp); - break; - - case Op_10exp: - compiler->addFunctionCall(&op_10exp); - break; - - default: - break; - } -} - -unsigned int OperatorBlocks::op_ln(VirtualMachine *vm) -{ - const Value &v = *vm->getInput(0, 1); - if (v < 0) - vm->replaceReturnValue(std::numeric_limits::quiet_NaN(), 1); - else if (v == 0 || v.isNaN()) - vm->replaceReturnValue(-std::numeric_limits::infinity(), 1); - else if (!v.isInfinity()) - vm->replaceReturnValue(std::log(v.toDouble()), 1); - return 0; -} - -unsigned int OperatorBlocks::op_log(VirtualMachine *vm) -{ - const Value &v = *vm->getInput(0, 1); - if (v < 0) - vm->replaceReturnValue(std::numeric_limits::quiet_NaN(), 1); - else if (v == 0 || v.isNaN()) - vm->replaceReturnValue(-std::numeric_limits::infinity(), 1); - else if (!v.isInfinity()) - vm->replaceReturnValue(std::log10(v.toDouble()), 1); - return 0; -} - -unsigned int OperatorBlocks::op_eexp(VirtualMachine *vm) -{ - const Value *v = vm->getInput(0, 1); - if (v->isNaN()) - vm->replaceReturnValue(1, 1); - else if (v->isNegativeInfinity()) - vm->replaceReturnValue(0, 1); - else if (!v->isInfinity()) - vm->replaceReturnValue(std::exp(v->toDouble()), 1); - return 0; -} - -unsigned int OperatorBlocks::op_10exp(VirtualMachine *vm) -{ - const Value *v = vm->getInput(0, 1); - if (v->isNaN()) - vm->replaceReturnValue(1, 1); - else if (v->isNegativeInfinity()) - vm->replaceReturnValue(0, 1); - else if (!v->isInfinity()) - vm->replaceReturnValue(std::pow(10, v->toDouble()), 1); - return 0; -} diff --git a/src/blocks/operatorblocks.h b/src/blocks/operatorblocks.h deleted file mode 100644 index e9dd932a..00000000 --- a/src/blocks/operatorblocks.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -namespace libscratchcpp -{ - -class Compiler; -class VirtualMachine; - -/*! \brief The OperatorBlocks class contains the implementation of operator blocks. */ -class OperatorBlocks : public IExtension -{ - public: - enum Inputs - { - NUM1, - NUM2, - FROM, - TO, - OPERAND1, - OPERAND2, - OPERAND, - STRING1, - STRING2, - LETTER, - STRING, - NUM - }; - - enum Fields - { - OPERATOR - }; - - enum FieldValues - { - Abs, - Floor, - Ceiling, - Sqrt, - Sin, - Cos, - Tan, - Asin, - Acos, - Atan, - Ln, - Log, - Eexp, - Op_10exp - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - static void compileAdd(Compiler *compiler); - static void compileSubtract(Compiler *compiler); - static void compileMultiply(Compiler *compiler); - static void compileDivide(Compiler *compiler); - static void compilePickRandom(Compiler *compiler); - static void compileLessThan(Compiler *compiler); - static void compileEquals(Compiler *compiler); - static void compileGreaterThan(Compiler *compiler); - static void compileAnd(Compiler *compiler); - static void compileOr(Compiler *compiler); - static void compileNot(Compiler *compiler); - static void compileJoin(Compiler *compiler); - static void compileLetterOf(Compiler *compiler); - static void compileLength(Compiler *compiler); - static void compileContains(Compiler *compiler); - static void compileMod(Compiler *compiler); - static void compileRound(Compiler *compiler); - static void compileMathOp(Compiler *compiler); - - static unsigned int op_ln(VirtualMachine *vm); - static unsigned int op_log(VirtualMachine *vm); - static unsigned int op_eexp(VirtualMachine *vm); - static unsigned int op_10exp(VirtualMachine *vm); -}; - -} // namespace libscratchcpp diff --git a/src/blocks/sensingblocks.cpp b/src/blocks/sensingblocks.cpp deleted file mode 100644 index af1349ed..00000000 --- a/src/blocks/sensingblocks.cpp +++ /dev/null @@ -1,1095 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sensingblocks.h" - -#include "../engine/internal/clock.h" -#include "audio/audioinput.h" -#include "audio/iaudioloudness.h" - -using namespace libscratchcpp; - -IClock *SensingBlocks::clock = nullptr; -IAudioInput *SensingBlocks::audioInput = nullptr; - -std::string SensingBlocks::name() const -{ - return "Sensing"; -} - -std::string SensingBlocks::description() const -{ - return name() + " blocks"; -} - -void SensingBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "sensing_touchingobject", &compileTouchingObject); - engine->addCompileFunction(this, "sensing_touchingcolor", &compileTouchingColor); - engine->addCompileFunction(this, "sensing_coloristouchingcolor", &compileColorIsTouchingColor); - engine->addCompileFunction(this, "sensing_distanceto", &compileDistanceTo); - engine->addCompileFunction(this, "sensing_askandwait", &compileAskAndWait); - engine->addCompileFunction(this, "sensing_answer", &compileAnswer); - engine->addCompileFunction(this, "sensing_keypressed", &compileKeyPressed); - engine->addCompileFunction(this, "sensing_mousedown", &compileMouseDown); - engine->addCompileFunction(this, "sensing_mousex", &compileMouseX); - engine->addCompileFunction(this, "sensing_mousey", &compileMouseY); - engine->addCompileFunction(this, "sensing_setdragmode", &compileSetDragMode); - engine->addCompileFunction(this, "sensing_loudness", &compileLoudness); - engine->addCompileFunction(this, "sensing_loud", &compileLoud); - engine->addCompileFunction(this, "sensing_timer", &compileTimer); - engine->addCompileFunction(this, "sensing_resettimer", &compileResetTimer); - engine->addCompileFunction(this, "sensing_of", &compileOf); - engine->addCompileFunction(this, "sensing_current", &compileCurrent); - engine->addCompileFunction(this, "sensing_dayssince2000", &compileDaysSince2000); - - // Monitor names - engine->addMonitorNameFunction(this, "sensing_mousedown", &mouseDownMonitorName); - engine->addMonitorNameFunction(this, "sensing_mousex", &mouseXMonitorName); - engine->addMonitorNameFunction(this, "sensing_mousey", &mouseYMonitorName); - engine->addMonitorNameFunction(this, "sensing_loudness", &loudnessMonitorName); - engine->addMonitorNameFunction(this, "sensing_timer", &timerMonitorName); - engine->addMonitorNameFunction(this, "sensing_current", ¤tMonitorName); - engine->addMonitorNameFunction(this, "sensing_dayssince2000", &daysSince2000MonitorName); - - // Inputs - engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU); - engine->addInput(this, "COLOR", COLOR); - engine->addInput(this, "COLOR2", COLOR2); - engine->addInput(this, "DISTANCETOMENU", DISTANCETOMENU); - engine->addInput(this, "QUESTION", QUESTION); - engine->addInput(this, "KEY_OPTION", KEY_OPTION); - engine->addInput(this, "OBJECT", OBJECT); - - // Fields - engine->addField(this, "CURRENTMENU", CURRENTMENU); - engine->addField(this, "DRAG_MODE", DRAG_MODE); - engine->addField(this, "PROPERTY", PROPERTY); - - // Field values - engine->addFieldValue(this, "YEAR", YEAR); - engine->addFieldValue(this, "MONTH", MONTH); - engine->addFieldValue(this, "DATE", DATE); - engine->addFieldValue(this, "DAYOFWEEK", DAYOFWEEK); - engine->addFieldValue(this, "HOUR", HOUR); - engine->addFieldValue(this, "MINUTE", MINUTE); - engine->addFieldValue(this, "SECOND", SECOND); - engine->addFieldValue(this, "draggable", Draggable); - engine->addFieldValue(this, "not draggable", NotDraggable); - engine->addFieldValue(this, "x position", XPosition); - engine->addFieldValue(this, "y position", YPosition); - engine->addFieldValue(this, "direction", Direction); - engine->addFieldValue(this, "costume #", CostumeNumber); - engine->addFieldValue(this, "costume name", CostumeName); - engine->addFieldValue(this, "size", Size); - engine->addFieldValue(this, "volume", Volume); - engine->addFieldValue(this, "background #", BackdropNumber); // Scratch 1.4 support - engine->addFieldValue(this, "backdrop #", BackdropNumber); - engine->addFieldValue(this, "backdrop name", BackdropName); - - // Callbacks - engine->questionAnswered().connect(&onAnswer); -} - -void SensingBlocks::onInit(IEngine *engine) -{ - engine->threadAboutToStop().connect([engine](Thread *thread) { - if (!m_questionList.empty()) { - // Abort the question of this thread if it's currently being displayed - if (m_questionList.front()->vm == thread->vm()) { - thread->target()->bubble()->setText(""); - engine->questionAborted()(); - } - - m_questionList.erase( - std::remove_if(m_questionList.begin(), m_questionList.end(), [thread](const std::unique_ptr &question) { return question->vm == thread->vm(); }), - m_questionList.end()); - } - }); -} - -void SensingBlocks::compileTouchingObject(Compiler *compiler) -{ - Input *input = compiler->input(TOUCHINGOBJECTMENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&touchingMousePointer); - else if (value == "_edge_") - compiler->addFunctionCall(&touchingEdge); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&touchingObjectByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&touchingObject); - } -} - -void SensingBlocks::compileTouchingColor(Compiler *compiler) -{ - compiler->addInput(COLOR); - compiler->addFunctionCall(&touchingColor); -} - -void SensingBlocks::compileColorIsTouchingColor(Compiler *compiler) -{ - compiler->addInput(COLOR2); // target color - compiler->addInput(COLOR); // mask color - compiler->addFunctionCall(&colorIsTouchingColor); -} - -void SensingBlocks::compileDistanceTo(Compiler *compiler) -{ - Input *input = compiler->input(DISTANCETOMENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - if (value == "_mouse_") - compiler->addFunctionCall(&distanceToMousePointer); - else { - int index = compiler->engine()->findTarget(value); - compiler->addConstValue(index); - compiler->addFunctionCall(&distanceToByIndex); - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(&distanceTo); - } -} - -void SensingBlocks::compileAskAndWait(Compiler *compiler) -{ - compiler->addInput(QUESTION); - compiler->addFunctionCall(&askAndWait); -} - -void SensingBlocks::compileAnswer(Compiler *compiler) -{ - compiler->addFunctionCall(&answer); -} - -void SensingBlocks::compileKeyPressed(Compiler *compiler) -{ - compiler->addInput(KEY_OPTION); - compiler->addFunctionCall(&keyPressed); -} - -void SensingBlocks::compileMouseDown(Compiler *compiler) -{ - compiler->addFunctionCall(&mouseDown); -} - -void SensingBlocks::compileMouseX(Compiler *compiler) -{ - compiler->addFunctionCall(&mouseX); -} - -void SensingBlocks::compileMouseY(Compiler *compiler) -{ - compiler->addFunctionCall(&mouseY); -} - -void SensingBlocks::compileSetDragMode(Compiler *compiler) -{ - int option = compiler->field(DRAG_MODE)->specialValueId(); - - switch (option) { - case Draggable: - compiler->addFunctionCall(&setDraggableMode); - break; - - case NotDraggable: - compiler->addFunctionCall(&setNotDraggableMode); - break; - - default: - break; - } -} - -void SensingBlocks::compileLoudness(Compiler *compiler) -{ - compiler->addFunctionCall(&loudness); -} - -void SensingBlocks::compileLoud(Compiler *compiler) -{ - compiler->addFunctionCall(&loud); -} - -void SensingBlocks::compileTimer(Compiler *compiler) -{ - compiler->addFunctionCall(&timer); -} - -void SensingBlocks::compileResetTimer(Compiler *compiler) -{ - compiler->addFunctionCall(&resetTimer); -} - -void SensingBlocks::compileOf(Compiler *compiler) -{ - Field *property = compiler->field(PROPERTY); - assert(property); - int option = property->specialValueId(); - Input *input = compiler->input(OBJECT); - assert(input); - BlockFunc f = nullptr; - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - IEngine *engine = compiler->engine(); - assert(engine); - int index = engine->findTarget(value); - - if (index == -1) { - compiler->addInstruction(vm::OP_NULL); - return; - } - - switch (option) { - case XPosition: - f = &xPositionOfSpriteByIndex; - break; - - case YPosition: - f = &yPositionOfSpriteByIndex; - break; - - case Direction: - f = &directionOfSpriteByIndex; - break; - - case CostumeNumber: - f = &costumeNumberOfSpriteByIndex; - break; - - case CostumeName: - f = &costumeNameOfSpriteByIndex; - break; - - case Size: - f = &sizeOfSpriteByIndex; - break; - - case Volume: - f = &volumeOfTargetByIndex; - break; - - case BackdropNumber: - f = &backdropNumberOfStageByIndex; - break; - - case BackdropName: - f = &backdropNameOfStageByIndex; - break; - - default: { - // Variable - f = &variableOfTargetByIndex; - Target *target = engine->targetAt(index); - auto varIndex = target->findVariable(property->value().toString()); - - if (varIndex == -1) - compiler->addInstruction(vm::OP_NULL); - else { - // NOTE: The OP_READ_VAR instruction can't be used for this (see #548) - compiler->addConstValue(varIndex); - } - - break; - } - } - - if (f) - compiler->addConstValue(index); - } else { - switch (option) { - case XPosition: - f = &xPositionOfSprite; - break; - - case YPosition: - f = &yPositionOfSprite; - break; - - case Direction: - f = &directionOfSprite; - break; - - case CostumeNumber: - f = &costumeNumberOfSprite; - break; - - case CostumeName: - f = &costumeNameOfSprite; - break; - - case Size: - f = &sizeOfSprite; - break; - - case Volume: - f = &volumeOfTarget; - break; - - case BackdropNumber: - f = &backdropNumberOfStage; - break; - - case BackdropName: - f = &backdropNameOfStage; - break; - - default: - f = &variableOfTarget; - compiler->addConstValue(property->value().toString()); - break; - } - - if (f) - compiler->addInput(input); - } - - if (f) - compiler->addFunctionCall(f); -} - -void SensingBlocks::compileCurrent(Compiler *compiler) -{ - int id = compiler->field(CURRENTMENU)->specialValueId(); - - switch (id) { - case YEAR: - compiler->addFunctionCall(¤tYear); - break; - - case MONTH: - compiler->addFunctionCall(¤tMonth); - break; - - case DATE: - compiler->addFunctionCall(¤tDate); - break; - - case DAYOFWEEK: - compiler->addFunctionCall(¤tDayOfWeek); - break; - - case HOUR: - compiler->addFunctionCall(¤tHour); - break; - - case MINUTE: - compiler->addFunctionCall(¤tMinute); - break; - - case SECOND: - compiler->addFunctionCall(¤tSecond); - break; - - default: - break; - } -} - -void SensingBlocks::compileDaysSince2000(Compiler *compiler) -{ - compiler->addFunctionCall(&daysSince2000); -} - -const std::string &SensingBlocks::mouseDownMonitorName(Block *block) -{ - static const std::string name = "mouse down?"; - return name; -} - -const std::string &SensingBlocks::mouseXMonitorName(Block *block) -{ - static const std::string name = "mouse x"; - return name; -} - -const std::string &SensingBlocks::mouseYMonitorName(Block *block) -{ - static const std::string name = "mouse y"; - return name; -} - -const std::string &SensingBlocks::loudnessMonitorName(Block *block) -{ - static const std::string name = "loudness"; - return name; -} - -const std::string &SensingBlocks::timerMonitorName(Block *block) -{ - static const std::string name = "timer"; - return name; -} - -const std::string &SensingBlocks::currentMonitorName(Block *block) -{ - int id = block->findFieldById(CURRENTMENU)->specialValueId(); - - switch (id) { - case YEAR: { - static const std::string name = "year"; - return name; - } - - case MONTH: { - static const std::string name = "month"; - return name; - } - - case DATE: { - static const std::string name = "date"; - return name; - } - - case DAYOFWEEK: { - static const std::string name = "day of week"; - return name; - } - - case HOUR: { - static const std::string name = "hour"; - return name; - } - - case MINUTE: { - static const std::string name = "minute"; - return name; - } - - case SECOND: { - static const std::string name = "second"; - return name; - } - - default: { - static const std::string name = ""; - return name; - } - } -} - -const std::string &SensingBlocks::daysSince2000MonitorName(Block *block) -{ - static const std::string name = "days since 2000"; - return name; -} - -unsigned int SensingBlocks::touchingObject(VirtualMachine *vm) -{ - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1); - else if (value == "_edge_") - vm->replaceReturnValue(vm->target()->touchingEdge(), 1); - else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - vm->replaceReturnValue(touchingObjectCommon(vm->target(), target), 1); - } - - return 0; -} - -unsigned int SensingBlocks::touchingObjectByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - vm->replaceReturnValue(touchingObjectCommon(vm->target(), target), 1); - return 0; -} - -unsigned int SensingBlocks::touchingMousePointer(VirtualMachine *vm) -{ - vm->addReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY())); - return 0; -} - -unsigned int SensingBlocks::touchingEdge(VirtualMachine *vm) -{ - vm->addReturnValue(vm->target()->touchingEdge()); - return 0; -} - -unsigned int SensingBlocks::touchingColor(VirtualMachine *vm) -{ - vm->replaceReturnValue(vm->target()->touchingColor(vm->getInput(0, 1)->toRgba()), 1); - return 0; -} - -unsigned int SensingBlocks::colorIsTouchingColor(VirtualMachine *vm) -{ - vm->replaceReturnValue(vm->target()->touchingColor(vm->getInput(0, 2)->toRgba(), vm->getInput(1, 2)->toRgba()), 2); - return 1; -} - -unsigned int SensingBlocks::keyPressed(VirtualMachine *vm) -{ - vm->replaceReturnValue(vm->engine()->keyPressed(vm->getInput(0, 1)->toString()), 1); - return 0; -} - -unsigned int SensingBlocks::mouseDown(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->mousePressed()); - return 0; -} - -unsigned int SensingBlocks::mouseX(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->mouseX()); - return 0; -} - -unsigned int SensingBlocks::mouseY(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->mouseY()); - return 0; -} - -unsigned int SensingBlocks::setDraggableMode(VirtualMachine *vm) -{ - if (Sprite *sprite = dynamic_cast(vm->target())) - sprite->setDraggable(true); - - return 0; -} - -unsigned int SensingBlocks::setNotDraggableMode(VirtualMachine *vm) -{ - if (Sprite *sprite = dynamic_cast(vm->target())) - sprite->setDraggable(false); - - return 0; -} - -unsigned int SensingBlocks::loudness(VirtualMachine *vm) -{ - if (!audioInput) - audioInput = AudioInput::instance().get(); - - auto audioLoudness = audioInput->audioLoudness(); - vm->addReturnValue(audioLoudness->getLoudness()); - return 0; -} - -unsigned int SensingBlocks::loud(VirtualMachine *vm) -{ - if (!audioInput) - audioInput = AudioInput::instance().get(); - - auto audioLoudness = audioInput->audioLoudness(); - vm->addReturnValue(audioLoudness->getLoudness() > 10.0); - return 0; -} - -unsigned int SensingBlocks::distanceTo(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (!sprite) { - vm->replaceReturnValue(10000, 1); - return 0; - } - - std::string value = vm->getInput(0, 1)->toString(); - - if (value == "_mouse_") - vm->replaceReturnValue(std::sqrt(std::pow(sprite->x() - vm->engine()->mouseX(), 2) + std::pow(sprite->y() - vm->engine()->mouseY(), 2)), 1); - else { - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value)); - Sprite *targetSprite = dynamic_cast(target); - - if (targetSprite) - vm->replaceReturnValue(std::sqrt(std::pow(sprite->x() - targetSprite->x(), 2) + std::pow(sprite->y() - targetSprite->y(), 2)), 1); - else - vm->replaceReturnValue(10000, 1); - } - - return 0; -} - -unsigned int SensingBlocks::distanceToByIndex(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *targetSprite = dynamic_cast(target); - - if (sprite && targetSprite) - vm->replaceReturnValue(std::sqrt(std::pow(sprite->x() - targetSprite->x(), 2) + std::pow(sprite->y() - targetSprite->y(), 2)), 1); - else - vm->replaceReturnValue(10000, 1); - - return 0; -} - -unsigned int SensingBlocks::distanceToMousePointer(VirtualMachine *vm) -{ - Sprite *sprite = dynamic_cast(vm->target()); - - if (sprite) - vm->addReturnValue(std::sqrt(std::pow(sprite->x() - vm->engine()->mouseX(), 2) + std::pow(sprite->y() - vm->engine()->mouseY(), 2))); - else - vm->addReturnValue(10000); - - return 0; -} - -void SensingBlocks::onAnswer(const std::string &answer) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L99-L115 - m_answer = answer; - - if (!m_questionList.empty()) { - Question *question = m_questionList.front().get(); - VirtualMachine *vm = question->vm; - assert(vm); - assert(vm->target()); - - // If the target was visible when asked, hide the say bubble unless the target was the stage - if (question->wasVisible && !question->wasStage) - vm->target()->bubble()->setText(""); - - m_questionList.erase(m_questionList.begin()); - vm->resolvePromise(); - askNextQuestion(); - } -} - -unsigned int SensingBlocks::askAndWait(VirtualMachine *vm) -{ - const bool isQuestionAsked = !m_questionList.empty(); - enqueueAsk(vm->getInput(0, 1)->toString(), vm); - - if (!isQuestionAsked) - askNextQuestion(); - - vm->promise(); - return 1; -} - -unsigned int SensingBlocks::answer(VirtualMachine *vm) -{ - vm->addReturnValue(m_answer); - return 0; -} - -unsigned int SensingBlocks::timer(VirtualMachine *vm) -{ - vm->addReturnValue(vm->engine()->timer()->value()); - return 0; -} - -unsigned int SensingBlocks::resetTimer(VirtualMachine *vm) -{ - vm->engine()->timer()->reset(); - return 0; -} - -unsigned int SensingBlocks::xPositionOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->x(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::xPositionOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->x(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::yPositionOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->y(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::yPositionOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->y(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::directionOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->direction(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::directionOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->direction(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNumberOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNumberOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNameOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::costumeNameOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::sizeOfSprite(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->size(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::sizeOfSpriteByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Sprite *sprite = dynamic_cast(target); - - if (sprite) - vm->replaceReturnValue(sprite->size(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::volumeOfTarget(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - - if (target) - vm->replaceReturnValue(target->volume(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::volumeOfTargetByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - - if (target) - vm->replaceReturnValue(target->volume(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::variableOfTarget(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(1, 2)->toString())); - - if (target) { - auto varIndex = target->findVariable(vm->getInput(0, 2)->toString()); - - if (varIndex == -1) - vm->replaceReturnValue(0, 2); - else - vm->replaceReturnValue(target->variableAt(varIndex)->value(), 2); - } else - vm->replaceReturnValue(0, 2); - - return 1; -} - -unsigned int SensingBlocks::variableOfTargetByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - - if (target) { - const int varIndex = vm->getInput(0, 2)->toInt(); - vm->replaceReturnValue(target->variableAt(varIndex)->value(), 2); - } else - vm->replaceReturnValue(0, 2); - - return 1; -} - -unsigned int SensingBlocks::backdropNumberOfStage(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::backdropNumberOfStageByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->costumeIndex() + 1, 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::backdropNameOfStage(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString())); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::backdropNameOfStageByIndex(VirtualMachine *vm) -{ - Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); - Stage *stage = dynamic_cast(target); - - if (stage) - vm->replaceReturnValue(stage->currentCostume()->name(), 1); - else - vm->replaceReturnValue(0, 1); - - return 0; -} - -unsigned int SensingBlocks::currentYear(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_year + 1900); - - return 0; -} - -unsigned int SensingBlocks::currentMonth(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_mon + 1); - - return 0; -} - -unsigned int SensingBlocks::currentDate(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_mday); - - return 0; -} - -unsigned int SensingBlocks::currentDayOfWeek(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_wday + 1); - - return 0; -} - -unsigned int SensingBlocks::currentHour(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_hour); - - return 0; -} - -unsigned int SensingBlocks::currentMinute(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_min); - - return 0; -} - -unsigned int SensingBlocks::currentSecond(VirtualMachine *vm) -{ - time_t now = time(0); - tm *ltm = localtime(&now); - vm->addReturnValue(ltm->tm_sec); - - return 0; -} - -unsigned int SensingBlocks::daysSince2000(VirtualMachine *vm) -{ - if (!clock) - clock = Clock::instance().get(); - - auto ms = std::chrono::duration_cast(clock->currentSystemTime().time_since_epoch()).count(); - vm->addReturnValue(ms / 86400000.0 - 10957); - - return 0; -} - -bool SensingBlocks::touchingObjectCommon(Target *source, Target *target) -{ - if (source && target && !target->isStage()) - return source->touchingSprite(static_cast(target)); - - return false; -} - -void SensingBlocks::enqueueAsk(const std::string &question, VirtualMachine *vm) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L117-L119 - assert(vm); - Target *target = vm->target(); - assert(target); - bool visible = true; - bool isStage = target->isStage(); - - if (!isStage) { - Sprite *sprite = static_cast(target); - visible = sprite->visible(); - } - - m_questionList.push_back(std::make_unique(question, vm, visible, isStage)); -} - -void SensingBlocks::askNextQuestion() -{ - // https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L121-L133 - if (m_questionList.empty()) - return; - - Question *question = m_questionList.front().get(); - Target *target = question->vm->target(); - IEngine *engine = question->vm->engine(); - - // If the target is visible, emit a blank question and show - // a bubble unless the target was the stage - if (question->wasVisible && !question->wasStage) { - target->bubble()->setType(TextBubble::Type::Say); - target->bubble()->setText(question->question); - - engine->questionAsked()(""); - } else { - engine->questionAsked()(question->question); - } -} diff --git a/src/blocks/sensingblocks.h b/src/blocks/sensingblocks.h deleted file mode 100644 index 8153a18b..00000000 --- a/src/blocks/sensingblocks.h +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include - -#include "../engine/internal/clock.h" - -namespace libscratchcpp -{ - -class Target; -class IAudioInput; - -/*! \brief The SensingBlocks class contains the implementation of sensing blocks. */ -class SensingBlocks : public IExtension -{ - public: - enum Inputs - { - TOUCHINGOBJECTMENU, - COLOR, - COLOR2, - DISTANCETOMENU, - QUESTION, - KEY_OPTION, - OBJECT - }; - - enum Fields - { - CURRENTMENU, - DRAG_MODE, - PROPERTY - }; - - enum FieldValues - { - YEAR, - MONTH, - DATE, - DAYOFWEEK, - HOUR, - MINUTE, - SECOND, - Draggable, - NotDraggable, - XPosition, - YPosition, - Direction, - CostumeNumber, - CostumeName, - Size, - Volume, - BackdropNumber, - BackdropName - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static void compileTouchingObject(Compiler *compiler); - static void compileTouchingColor(Compiler *compiler); - static void compileColorIsTouchingColor(Compiler *compiler); - static void compileDistanceTo(Compiler *compiler); - static void compileAskAndWait(Compiler *compiler); - static void compileAnswer(Compiler *compiler); - static void compileKeyPressed(Compiler *compiler); - static void compileMouseDown(Compiler *compiler); - static void compileMouseX(Compiler *compiler); - static void compileMouseY(Compiler *compiler); - static void compileSetDragMode(Compiler *compiler); - static void compileLoudness(Compiler *compiler); - static void compileLoud(Compiler *compiler); - static void compileTimer(Compiler *compiler); - static void compileResetTimer(Compiler *compiler); - static void compileOf(Compiler *compiler); - static void compileCurrent(Compiler *compiler); - static void compileDaysSince2000(Compiler *compiler); - - static const std::string &mouseDownMonitorName(Block *block); - static const std::string &mouseXMonitorName(Block *block); - static const std::string &mouseYMonitorName(Block *block); - static const std::string &loudnessMonitorName(Block *block); - static const std::string &timerMonitorName(Block *block); - static const std::string ¤tMonitorName(Block *block); - static const std::string &daysSince2000MonitorName(Block *block); - - static unsigned int touchingObject(VirtualMachine *vm); - static unsigned int touchingObjectByIndex(VirtualMachine *vm); - static unsigned int touchingMousePointer(VirtualMachine *vm); - static unsigned int touchingEdge(VirtualMachine *vm); - - static unsigned int touchingColor(VirtualMachine *vm); - - static unsigned int colorIsTouchingColor(VirtualMachine *vm); - - static unsigned int keyPressed(VirtualMachine *vm); - static unsigned int mouseDown(VirtualMachine *vm); - static unsigned int mouseX(VirtualMachine *vm); - static unsigned int mouseY(VirtualMachine *vm); - - static unsigned int setDraggableMode(VirtualMachine *vm); - static unsigned int setNotDraggableMode(VirtualMachine *vm); - - static unsigned int loudness(VirtualMachine *vm); - static unsigned int loud(VirtualMachine *vm); - - static unsigned int distanceTo(VirtualMachine *vm); - static unsigned int distanceToByIndex(VirtualMachine *vm); - static unsigned int distanceToMousePointer(VirtualMachine *vm); - - static void onAnswer(const std::string &answer); - static unsigned int askAndWait(VirtualMachine *vm); - static unsigned int answer(VirtualMachine *vm); - - static unsigned int timer(VirtualMachine *vm); - static unsigned int resetTimer(VirtualMachine *vm); - - static unsigned int xPositionOfSprite(VirtualMachine *vm); - static unsigned int xPositionOfSpriteByIndex(VirtualMachine *vm); - static unsigned int yPositionOfSprite(VirtualMachine *vm); - static unsigned int yPositionOfSpriteByIndex(VirtualMachine *vm); - static unsigned int directionOfSprite(VirtualMachine *vm); - static unsigned int directionOfSpriteByIndex(VirtualMachine *vm); - static unsigned int costumeNumberOfSprite(VirtualMachine *vm); - static unsigned int costumeNumberOfSpriteByIndex(VirtualMachine *vm); - static unsigned int costumeNameOfSprite(VirtualMachine *vm); - static unsigned int costumeNameOfSpriteByIndex(VirtualMachine *vm); - static unsigned int sizeOfSprite(VirtualMachine *vm); - static unsigned int sizeOfSpriteByIndex(VirtualMachine *vm); - static unsigned int volumeOfTarget(VirtualMachine *vm); - static unsigned int volumeOfTargetByIndex(VirtualMachine *vm); - static unsigned int variableOfTarget(VirtualMachine *vm); - static unsigned int variableOfTargetByIndex(VirtualMachine *vm); - static unsigned int backdropNumberOfStage(VirtualMachine *vm); - static unsigned int backdropNumberOfStageByIndex(VirtualMachine *vm); - static unsigned int backdropNameOfStage(VirtualMachine *vm); - static unsigned int backdropNameOfStageByIndex(VirtualMachine *vm); - - static unsigned int currentYear(VirtualMachine *vm); - static unsigned int currentMonth(VirtualMachine *vm); - static unsigned int currentDate(VirtualMachine *vm); - static unsigned int currentDayOfWeek(VirtualMachine *vm); - static unsigned int currentHour(VirtualMachine *vm); - static unsigned int currentMinute(VirtualMachine *vm); - static unsigned int currentSecond(VirtualMachine *vm); - static unsigned int daysSince2000(VirtualMachine *vm); - - static IClock *clock; - static IAudioInput *audioInput; - - private: - struct Question - { - Question(const std::string &question, VirtualMachine *vm, bool wasVisible, bool wasStage) : - question(question), - vm(vm), - wasVisible(wasVisible), - wasStage(wasStage) - { - } - - std::string question; - VirtualMachine *vm = nullptr; - bool wasVisible = false; - bool wasStage = false; - }; - - static bool touchingObjectCommon(Target *source, Target *target); - - static void enqueueAsk(const std::string &question, VirtualMachine *vm); - static void askNextQuestion(); - - static inline std::vector> m_questionList; - static inline Value m_answer; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/soundblocks.cpp b/src/blocks/soundblocks.cpp deleted file mode 100644 index 6cc2aa7b..00000000 --- a/src/blocks/soundblocks.cpp +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include - -#include "soundblocks.h" - -using namespace libscratchcpp; - -// TODO: Use C++20 -template -static void erase_if(ContainerT &items, const PredicateT &predicate) -{ - for (auto it = items.begin(); it != items.end();) { - if (predicate(*it)) - it = items.erase(it); - else - ++it; - } -} - -std::string SoundBlocks::name() const -{ - return "Sound"; -} - -std::string SoundBlocks::description() const -{ - return name() + " blocks"; -} - -void SoundBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "sound_play", &compilePlay); - engine->addCompileFunction(this, "sound_playuntildone", &compilePlayUntilDone); - engine->addCompileFunction(this, "sound_stopallsounds", &compileStopAllSounds); - engine->addCompileFunction(this, "sound_seteffectto", &compileSetEffectTo); - engine->addCompileFunction(this, "sound_changeeffectby", &compileChangeEffectBy); - engine->addCompileFunction(this, "sound_changevolumeby", &compileChangeVolumeBy); - engine->addCompileFunction(this, "sound_cleareffects", &compileClearEffects); - engine->addCompileFunction(this, "sound_setvolumeto", &compileSetVolumeTo); - engine->addCompileFunction(this, "sound_volume", &compileVolume); - - // Monitor names - engine->addMonitorNameFunction(this, "sound_volume", &volumeMonitorName); - - // Inputs - engine->addInput(this, "SOUND_MENU", SOUND_MENU); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "VOLUME", VOLUME); - - // Fields - engine->addField(this, "EFFECT", EFFECT); - - // Field values - engine->addFieldValue(this, "PITCH", PITCH); - engine->addFieldValue(this, "PAN", PAN); -} - -void SoundBlocks::onInit(IEngine *engine) -{ - m_waitingSounds.clear(); - engine->threadAboutToStop().connect([](Thread *thread) { erase_if(m_waitingSounds, [thread](const std::pair &pair) { return pair.second == thread->vm(); }); }); -} - -bool SoundBlocks::compilePlayCommon(Compiler *compiler, bool untilDone, bool *byIndex) -{ - Target *target = compiler->target(); - assert(target); - - if (!target) - return false; - - Input *input = compiler->input(SOUND_MENU); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - - int index = target->findSound(value); - - if (index == -1) { - Value v(value); - - if (v.isValidNumber() && !v.isInfinity() && !v.isNegativeInfinity() && !(v.isString() && v.toString().empty())) { - compiler->addConstValue(v.toLong() - 1); - compiler->addFunctionCall(untilDone ? &playByIndexUntilDone : &playByIndex); - - if (byIndex) - *byIndex = true; - - return true; - } - } else { - compiler->addConstValue(index); - compiler->addFunctionCall(untilDone ? &playByIndexUntilDone : &playByIndex); - - if (byIndex) - *byIndex = true; - - return true; - } - } else { - compiler->addInput(input); - compiler->addFunctionCall(untilDone ? &playUntilDone : &play); - - if (byIndex) - *byIndex = false; - - return true; - } - - return false; -} - -void SoundBlocks::compilePlay(Compiler *compiler) -{ - compilePlayCommon(compiler, false); -} - -void SoundBlocks::compilePlayUntilDone(Compiler *compiler) -{ - bool byIndex = false; - - if (compilePlayCommon(compiler, true, &byIndex)) - compiler->addFunctionCall(byIndex ? &checkSoundByIndex : &checkSound); -} - -void SoundBlocks::compileStopAllSounds(Compiler *compiler) -{ - compiler->addFunctionCall(&stopAllSounds); -} - -void SoundBlocks::compileSetEffectTo(Compiler *compiler) -{ - compiler->addInput(VALUE); - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case PITCH: - compiler->addFunctionCall(&setPitchEffectTo); - break; - - case PAN: - compiler->addFunctionCall(&setPanEffectTo); - break; - - default: - assert(false); - break; - } -} - -void SoundBlocks::compileChangeEffectBy(Compiler *compiler) -{ - compiler->addInput(VALUE); - int option = compiler->field(EFFECT)->specialValueId(); - - switch (option) { - case PITCH: - compiler->addFunctionCall(&changePitchEffectBy); - break; - - case PAN: - compiler->addFunctionCall(&changePanEffectBy); - break; - - default: - assert(false); - break; - } -} - -void SoundBlocks::compileClearEffects(Compiler *compiler) -{ - compiler->addFunctionCall(&clearEffects); -} - -void SoundBlocks::compileChangeVolumeBy(Compiler *compiler) -{ - compiler->addInput(VOLUME); - compiler->addFunctionCall(&changeVolumeBy); -} - -void SoundBlocks::compileSetVolumeTo(Compiler *compiler) -{ - compiler->addInput(VOLUME); - compiler->addFunctionCall(&setVolumeTo); -} - -void SoundBlocks::compileVolume(Compiler *compiler) -{ - compiler->addFunctionCall(&volume); -} - -const std::string &SoundBlocks::volumeMonitorName(Block *block) -{ - static const std::string name = "volume"; - return name; -} - -Sound *SoundBlocks::getSoundByIndex(Target *target, long index) -{ - long soundCount = target->sounds().size(); - - if (index < 0 || index >= soundCount) { - if (index < 0) - index = std::fmod(soundCount + std::fmod(index, -soundCount), soundCount); - else - index = std::fmod(index, soundCount); - } - - return target->soundAt(index).get(); -} - -Sound *SoundBlocks::playCommon(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - const Value *name = vm->getInput(0, 1); - - if (target) { - Sound *sound = target->soundAt(target->findSound(name->toString())).get(); - - if (sound) { - sound->start(); - return sound; - } - - if (name->isValidNumber() && !name->isInfinity() && !name->isNegativeInfinity() && !(name->isString() && name->toString().empty())) { - sound = getSoundByIndex(target, name->toLong() - 1); - - if (sound) { - sound->start(); - return sound; - } - } - } - - return nullptr; -} - -Sound *SoundBlocks::playByIndexCommon(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - - if (target) { - Sound *sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt()); - - if (sound) { - sound->start(); - return sound; - } - } - - return nullptr; -} - -unsigned int SoundBlocks::play(VirtualMachine *vm) -{ - Sound *sound = playCommon(vm); - - if (sound) - m_waitingSounds.erase(sound); - - return 1; -} - -unsigned int SoundBlocks::playByIndex(VirtualMachine *vm) -{ - Sound *sound = playByIndexCommon(vm); - - if (sound) - m_waitingSounds.erase(sound); - - return 1; -} - -unsigned int SoundBlocks::playUntilDone(VirtualMachine *vm) -{ - Sound *sound = playCommon(vm); - - if (sound) - m_waitingSounds[sound] = vm; - - return 0; // leave the register for checkSound() -} - -unsigned int SoundBlocks::playByIndexUntilDone(VirtualMachine *vm) -{ - Sound *sound = playByIndexCommon(vm); - - if (sound) - m_waitingSounds[sound] = vm; - - return 0; // leave the register for checkSoundByIndex() -} - -unsigned int SoundBlocks::checkSound(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - const Value *name = vm->getInput(0, 1); - - if (target) { - Sound *sound = target->soundAt(target->findSound(name->toString())).get(); - - if (!sound && name->isValidNumber() && !name->isInfinity() && !name->isNegativeInfinity() && !(name->isString() && name->toString().empty())) - sound = getSoundByIndex(target, name->toLong() - 1); - - if (sound) { - auto it = m_waitingSounds.find(sound); - - if (it != m_waitingSounds.cend() && it->second == vm) { - if (sound->isPlaying()) - vm->stop(true, true, true); - else - m_waitingSounds.erase(sound); - } - } - } - - return 1; -} - -unsigned int SoundBlocks::checkSoundByIndex(VirtualMachine *vm) -{ - Target *target = vm->target(); - assert(target); - - if (target) { - auto sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt()); - - if (sound) { - auto it = m_waitingSounds.find(sound); - - if (it != m_waitingSounds.cend() && it->second == vm) { - if (sound->isPlaying()) - vm->stop(true, true, true); - else - m_waitingSounds.erase(sound); - } - } - } - - return 1; -} - -unsigned int SoundBlocks::stopAllSounds(VirtualMachine *vm) -{ - vm->engine()->stopSounds(); - m_waitingSounds.clear(); - return 0; -} - -unsigned int SoundBlocks::setPitchEffectTo(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pitch, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::setPanEffectTo(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pan, vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::changePitchEffectBy(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pitch, target->soundEffectValue(Sound::Effect::Pitch) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::changePanEffectBy(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setSoundEffectValue(Sound::Effect::Pan, target->soundEffectValue(Sound::Effect::Pan) + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::clearEffects(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->clearSoundEffects(); - - return 0; -} - -unsigned int SoundBlocks::changeVolumeBy(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setVolume(target->volume() + vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::setVolumeTo(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - target->setVolume(vm->getInput(0, 1)->toDouble()); - - return 1; -} - -unsigned int SoundBlocks::volume(VirtualMachine *vm) -{ - if (Target *target = vm->target()) - vm->addReturnValue(target->volume()); - else - vm->addReturnValue(0); - - return 0; -} diff --git a/src/blocks/soundblocks.h b/src/blocks/soundblocks.h deleted file mode 100644 index d31b8a62..00000000 --- a/src/blocks/soundblocks.h +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include - -namespace libscratchcpp -{ - -class Sound; -class Target; - -/*! \brief The SoundBlocks class contains the implementation of sound blocks. */ -class SoundBlocks : public IExtension -{ - public: - enum Inputs - { - SOUND_MENU, - VALUE, - VOLUME - }; - - enum Fields - { - EFFECT - }; - - enum FieldValues - { - PITCH, - PAN - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - void onInit(IEngine *engine) override; - - static bool compilePlayCommon(Compiler *compiler, bool untilDone, bool *byIndex = nullptr); - static void compilePlay(Compiler *compiler); - static void compilePlayUntilDone(Compiler *compiler); - static void compileStopAllSounds(Compiler *compiler); - static void compileSetEffectTo(Compiler *compiler); - static void compileChangeEffectBy(Compiler *compiler); - static void compileClearEffects(Compiler *compiler); - static void compileChangeVolumeBy(Compiler *compiler); - static void compileSetVolumeTo(Compiler *compiler); - static void compileVolume(Compiler *compiler); - - static const std::string &volumeMonitorName(Block *block); - - static Sound *getSoundByIndex(Target *target, long index); - static Sound *playCommon(VirtualMachine *vm); - static Sound *playByIndexCommon(VirtualMachine *vm); - - static unsigned int play(VirtualMachine *vm); - static unsigned int playByIndex(VirtualMachine *vm); - - static unsigned int playUntilDone(VirtualMachine *vm); - static unsigned int playByIndexUntilDone(VirtualMachine *vm); - static unsigned int checkSound(VirtualMachine *vm); - static unsigned int checkSoundByIndex(VirtualMachine *vm); - - static unsigned int stopAllSounds(VirtualMachine *vm); - - static unsigned int setPitchEffectTo(VirtualMachine *vm); - static unsigned int setPanEffectTo(VirtualMachine *vm); - static unsigned int changePitchEffectBy(VirtualMachine *vm); - static unsigned int changePanEffectBy(VirtualMachine *vm); - static unsigned int clearEffects(VirtualMachine *vm); - - static unsigned int changeVolumeBy(VirtualMachine *vm); - static unsigned int setVolumeTo(VirtualMachine *vm); - static unsigned int volume(VirtualMachine *vm); - - static inline std::unordered_map m_waitingSounds; -}; - -} // namespace libscratchcpp diff --git a/src/blocks/variableblocks.cpp b/src/blocks/variableblocks.cpp deleted file mode 100644 index 81961e49..00000000 --- a/src/blocks/variableblocks.cpp +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "variableblocks.h" - -using namespace libscratchcpp; - -std::string VariableBlocks::name() const -{ - return "Variables"; -} - -std::string VariableBlocks::description() const -{ - return "Variable blocks"; -} - -void VariableBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "data_variable", &compileVariable); - engine->addCompileFunction(this, "data_setvariableto", &compileSetVariable); - engine->addCompileFunction(this, "data_changevariableby", &compileChangeVariableBy); - engine->addCompileFunction(this, "data_showvariable", &compileShowVariable); - engine->addCompileFunction(this, "data_hidevariable", &compileHideVariable); - - // Monitor names - engine->addMonitorNameFunction(this, "data_variable", &variableMonitorName); - - // Monitor change functions - engine->addMonitorChangeFunction(this, "data_variable", &changeVariableMonitorValue); - - // Inputs - engine->addInput(this, "VALUE", VALUE); - - // Fields - engine->addField(this, "VARIABLE", VARIABLE); -} - -void VariableBlocks::compileVariable(Compiler *compiler) -{ - // NOTE: This block is only used by variable monitors - compiler->addInstruction(vm::OP_READ_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); -} - -void VariableBlocks::compileSetVariable(Compiler *compiler) -{ - compiler->addInput(VALUE); - compiler->addInstruction(vm::OP_SET_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); -} - -void VariableBlocks::compileChangeVariableBy(Compiler *compiler) -{ - compiler->addInput(VALUE); - compiler->addInstruction(vm::OP_CHANGE_VAR, { compiler->variableIndex(compiler->field(VARIABLE)->valuePtr()) }); -} - -void VariableBlocks::compileShowVariable(Compiler *compiler) -{ - Field *field = compiler->field(VARIABLE); - assert(field); - Variable *var = static_cast(field->valuePtr().get()); - assert(var); - - compiler->addConstValue(var->id()); - - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&showGlobalVariable); - else - compiler->addFunctionCall(&showVariable); -} - -void VariableBlocks::compileHideVariable(Compiler *compiler) -{ - Field *field = compiler->field(VARIABLE); - assert(field); - Variable *var = static_cast(field->valuePtr().get()); - assert(var); - - compiler->addConstValue(var->id()); - - if (var->target() == static_cast(compiler->engine()->stage())) - compiler->addFunctionCall(&hideGlobalVariable); - else - compiler->addFunctionCall(&hideVariable); -} - -void VariableBlocks::setVarVisible(std::shared_ptr var, bool visible, IEngine *engine) -{ - if (var) { - Monitor *monitor = var->monitor(); - - if (!monitor) - monitor = engine->createVariableMonitor(var, "data_variable", "VARIABLE", VARIABLE, &compileVariable); - - monitor->setVisible(visible); - } -} - -unsigned int VariableBlocks::showGlobalVariable(VirtualMachine *vm) -{ - if (Stage *target = vm->engine()->stage()) { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), true, vm->engine()); - } - - return 1; -} - -unsigned int VariableBlocks::showVariable(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root variable - int index = sprite->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(sprite->variableAt(index), true, vm->engine()); - } else { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), true, vm->engine()); - } - } - - return 1; -} - -unsigned int VariableBlocks::hideGlobalVariable(VirtualMachine *vm) -{ - if (Stage *target = vm->engine()->stage()) { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), false, vm->engine()); - } - - return 1; -} - -unsigned int VariableBlocks::hideVariable(VirtualMachine *vm) -{ - if (Target *target = vm->target()) { - if (!target->isStage() && static_cast(target)->isClone()) { - Sprite *sprite = static_cast(target)->cloneSprite(); // use clone root variable - int index = sprite->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(sprite->variableAt(index), false, vm->engine()); - } else { - int index = target->findVariableById(vm->getInput(0, 1)->toString()); - setVarVisible(target->variableAt(index), false, vm->engine()); - } - } - - return 1; -} - -const std::string &VariableBlocks::variableMonitorName(Block *block) -{ - Variable *var = dynamic_cast(block->findFieldById(VARIABLE)->valuePtr().get()); - - if (var) - return var->name(); - else { - static const std::string empty = ""; - return empty; - } -} - -void VariableBlocks::changeVariableMonitorValue(Block *block, const Value &newValue) -{ - Variable *var = dynamic_cast(block->findFieldById(VARIABLE)->valuePtr().get()); - - if (var) - var->setValue(newValue); -} diff --git a/src/blocks/variableblocks.h b/src/blocks/variableblocks.h deleted file mode 100644 index 2372660b..00000000 --- a/src/blocks/variableblocks.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -namespace libscratchcpp -{ - -class Compiler; -class Variable; - -/*! \brief The VariableBlocks class contains the implementation of variable blocks. */ -class VariableBlocks : public IExtension -{ - public: - enum Inputs - { - VALUE - }; - - enum Fields - { - VARIABLE - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(IEngine *engine) override; - - static void compileVariable(Compiler *compiler); - static void compileSetVariable(Compiler *compiler); - static void compileChangeVariableBy(Compiler *compiler); - static void compileShowVariable(Compiler *compiler); - static void compileHideVariable(Compiler *compiler); - - static void setVarVisible(std::shared_ptr var, bool visible, IEngine *engine); - - static unsigned int showGlobalVariable(VirtualMachine *vm); - static unsigned int showVariable(VirtualMachine *vm); - static unsigned int hideGlobalVariable(VirtualMachine *vm); - static unsigned int hideVariable(VirtualMachine *vm); - - static const std::string &variableMonitorName(Block *block); - static void changeVariableMonitorValue(Block *block, const Value &newValue); -}; - -} // namespace libscratchcpp diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index d34513d7..e97ee618 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -1,8 +1,5 @@ target_sources(scratchcpp PRIVATE - virtualmachine.cpp - virtualmachine_p.cpp - virtualmachine_p.h script.cpp script_p.cpp script_p.h diff --git a/src/engine/compiler.cpp b/src/engine/compiler.cpp deleted file mode 100644 index 2ff823ba..00000000 --- a/src/engine/compiler.cpp +++ /dev/null @@ -1,385 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include -#include -#include - -#include "compiler_p.h" - -using namespace libscratchcpp; -using namespace vm; - -/*! Constructs Compiler. */ -Compiler::Compiler(IEngine *engine, Target *target) : - impl(spimpl::make_unique_impl(engine, target)) -{ -} - -/*! Initializes the Compiler. Useful if you don't use compile(). */ -void Compiler::init() -{ - if (impl->initialized) - return; - - impl->bytecode.clear(); - impl->procedurePrototype = nullptr; - impl->warp = false; - - // Add start instruction - addInstruction(OP_START); - - impl->initialized = true; -} - -/*! Compiles the script. Use bytecode() to read the generated bytecode. */ -void Compiler::compile(std::shared_ptr topLevelBlock) -{ - init(); - - impl->block = topLevelBlock; - - // If this is a top-level block, it might be an edge-activated hat block with a predicate compile function - if (impl->block->topLevel()) { - HatPredicateCompileFunc f = impl->block->hatPredicateCompileFunction(); - - if (f) { - f(this); - - // Workaround for register leak warning spam: pause the script after getting the reported value - addFunctionCall([](VirtualMachine *vm) -> unsigned int { - vm->stop(false, false, false); - return 0; - }); - - end(); // finished with the predicate - impl->hatPredicateBytecode = impl->bytecode; - init(); // now start the real compilation - } - } - - while (impl->block) { - size_t substacks = impl->substackTree.size(); - - if (impl->block->compileFunction()) - impl->block->compile(this); - else { - std::cout << "warning: unsupported block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - } - - if (substacks != impl->substackTree.size()) - continue; - - if (impl->block) - impl->block = impl->block->next(); - - if (!impl->block && !impl->substackTree.empty()) - impl->substackEnd(); - } - - end(); -} - -/*! Finalizes the bytecode. Useful if you don't use compile(). */ -void Compiler::end() -{ - if (!impl->initialized) - return; - - // Add end instruction (halt) - addInstruction(OP_HALT); - - impl->initialized = false; -} - -/*! Returns the generated bytecode. */ -const std::vector &Compiler::bytecode() const -{ - return impl->bytecode; -} - -/*! Returns the generated hat predicate bytecode (if this is an edge-activated hat). */ -const std::vector &Compiler::hatPredicateBytecode() const -{ - return impl->hatPredicateBytecode; -} - -/*! Returns the Engine. */ -IEngine *Compiler::engine() const -{ - return impl->engine; -} - -Target *Compiler::target() const -{ - return impl->target; -} - -/*! Returns the list of constant input values. */ -const std::vector &Compiler::constInputValues() const -{ - return impl->constValues; -} - -/*! Returns the list of constant values. */ -std::vector Compiler::constValues() const -{ - std::vector ret; - for (auto value : impl->constValues) { - const auto &menuInfo = impl->constValueMenuInfo.at(value); - - if (menuInfo.first) - ret.push_back(menuInfo.second); - else - ret.push_back(value->value()); - } - return ret; -} - -/*! Returns the list of variables. */ -const std::vector &Compiler::variables() const -{ - return impl->variables; -} - -/*! Returns the list of pointers to variable values. */ -std::vector Compiler::variablePtrs() const -{ - std::vector ret; - for (auto var : impl->variables) - ret.push_back(var->valuePtr()); - return ret; -} - -/*! Returns the list of lists. */ -const std::vector &Compiler::lists() const -{ - return impl->lists; -} - -/*! Adds an instruction to the bytecode. */ -void Compiler::addInstruction(Opcode opcode, const std::initializer_list &args) -{ - impl->addInstruction(opcode, args); -} - -/*! Compiles the given input and adds it to the bytecode. */ -void Compiler::addInput(Input *input) -{ - if (!input) { - addInstruction(OP_NULL); - return; - } - switch (input->type()) { - case Input::Type::Shadow: - case Input::Type::NoShadow: { - if (input->pointsToDropdownMenu()) - addInstruction(OP_CONST, { impl->constIndex(input->primaryValue(), true, input->selectedMenuItem()) }); - else { - auto previousBlock = impl->block; - impl->block = input->valueBlock(); - - if (impl->block) { - if (impl->block->compileFunction()) - impl->block->compile(this); - else { - std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - addInstruction(OP_NULL); - } - } else - addInstruction(OP_CONST, { impl->constIndex(input->primaryValue()) }); - - impl->block = previousBlock; - } - - break; - } - - case Input::Type::ObscuredShadow: { - auto previousBlock = impl->block; - impl->block = input->valueBlock(); - if (impl->block) { - if (impl->block->compileFunction()) - impl->block->compile(this); - else { - std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - addInstruction(OP_NULL); - } - } else - input->primaryValue()->compile(this); - impl->block = previousBlock; - break; - } - } -} - -/*! Compiles the given input (resolved by ID) and adds it to the bytecode. */ -void Compiler::addInput(int id) -{ - addInput(input(id)); -} - -/*! Adds a constant value and an instruction to load it. Useful if you don't have an input for the addInput() method. */ -void libscratchcpp::Compiler::addConstValue(const Value &value) -{ - impl->customConstValues.push_back(std::make_unique()); - impl->customConstValues.back()->setValue(value); - addInstruction(OP_CONST, { impl->constIndex(impl->customConstValues.back().get()) }); -} - -/*! Adds a function call to the bytecode (the OP_EXEC instruction). */ -void Compiler::addFunctionCall(BlockFunc f) -{ - addInstruction(OP_EXEC, { impl->engine->functionIndex(f) }); -} - -/*! Adds an argument to a procedure (custom block). */ -void Compiler::addProcedureArg(const std::string &procCode, const std::string &argName) -{ - if (impl->procedureArgs.find(procCode) != impl->procedureArgs.cend()) { - const auto &procedure = impl->procedureArgs[procCode]; - - if (std::find(procedure.begin(), procedure.end(), argName) != procedure.end()) - return; - } - - impl->procedureArgs[procCode].push_back(argName); -} - -/*! Jumps to the given substack. The second substack is used for example for the if/else block. */ -void Compiler::moveToSubstack(std::shared_ptr substack1, std::shared_ptr substack2, SubstackType type) -{ - impl->substackTree.push_back({ { impl->block, substack2 }, type }); - impl->block = substack1; - if (!impl->block) - impl->substackEnd(); -} - -/*! Jumps to the given substack. */ -void Compiler::moveToSubstack(std::shared_ptr substack, SubstackType type) -{ - moveToSubstack(substack, nullptr, type); -} - -/*! Makes current script run without screen refresh. */ -void Compiler::warp() -{ - impl->warp = true; - addInstruction(vm::OP_WARP); -} - -/*! Returns the input with the given ID. */ -Input *Compiler::input(int id) const -{ - return impl->block->findInputById(id); -} - -/*! Returns the field with the given ID. */ -Field *Compiler::field(int id) const -{ - return impl->block->findFieldById(id); -} - -/*! Returns the block in the given input. Same as input(id)->valueBlock(), but with a null pointer check. */ -std::shared_ptr Compiler::inputBlock(int id) const -{ - auto in = input(id); - return in ? in->valueBlock() : nullptr; -} - -/*! Returns the index of the given variable. */ -unsigned int Compiler::variableIndex(std::shared_ptr varEntity) -{ - assert(varEntity); - auto var = dynamic_cast(varEntity.get()); - assert(var); - auto it = std::find(impl->variables.begin(), impl->variables.end(), var); - if (it != impl->variables.end()) - return it - impl->variables.begin(); - impl->variables.push_back(var); - return impl->variables.size() - 1; -} - -/*! Returns the index of the given list. */ -unsigned int Compiler::listIndex(std::shared_ptr listEntity) -{ - auto list = dynamic_cast(listEntity.get()); - auto it = std::find(impl->lists.begin(), impl->lists.end(), list); - if (it != impl->lists.end()) - return it - impl->lists.begin(); - impl->lists.push_back(list); - return impl->lists.size() - 1; -} - -/*! Returns the index of the given constant input value. */ -unsigned int Compiler::constIndex(InputValue *value) -{ - return impl->constIndex(value); -} - -/*! Returns the index of the procedure code of the given block. */ -unsigned int Compiler::procedureIndex(const std::string &proc) -{ - auto it = std::find(impl->procedures.begin(), impl->procedures.end(), proc); - if (it != impl->procedures.end()) - return it - impl->procedures.begin(); - impl->procedures.push_back(proc); - return impl->procedures.size() - 1; -} - -/*! Returns the index of the argument of the given procedure (custom block). */ -long Compiler::procedureArgIndex(const std::string &procCode, const std::string &argName) -{ - if (impl->procedureArgs.count(procCode) == 0) { - std::cout << "warning: could not find custom block '" << procCode << "'" << std::endl; - return -1; - } - const std::vector args = impl->procedureArgs[procCode]; - auto it = std::find(args.begin(), args.end(), argName); - if (it != args.end()) - return it - args.begin(); - std::cout << "warning: could not find argument '" << argName << "' in custom block '" << procCode << "'" << std::endl; - return -1; -} - -/*! Returns the prototype of the current custom block. */ -BlockPrototype *Compiler::procedurePrototype() const -{ - return impl->procedurePrototype; -} - -/*! Sets the prototype of the current custom block. */ -void Compiler::setProcedurePrototype(BlockPrototype *prototype) -{ - impl->procedurePrototype = prototype; -} - -/*! Returns unsupported block opcodes which were found when compiling. */ -const std::unordered_set &libscratchcpp::Compiler::unsupportedBlocks() const -{ - return impl->unsupportedBlocks; -} - -/*! Returns the list of custom block prototypes. */ -const std::vector &Compiler::procedures() const -{ - return impl->procedures; -} - -/*! Returns the current block. */ -const std::shared_ptr &Compiler::block() const -{ - return impl->block; -} - -/*! Sets the current block. Useful if you don't use compile(). */ -void Compiler::setBlock(std::shared_ptr block) -{ - impl->block = block; -} diff --git a/src/engine/compiler_p.cpp b/src/engine/compiler_p.cpp deleted file mode 100644 index 7e3e5f1c..00000000 --- a/src/engine/compiler_p.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include - -#include "compiler_p.h" - -using namespace libscratchcpp; -using namespace vm; - -CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) : - engine(engine), - target(target) -{ - assert(engine); -} - -void CompilerPrivate::addInstruction(vm::Opcode opcode, std::initializer_list args) -{ - bytecode.push_back(opcode); - for (auto arg : args) - bytecode.push_back(arg); -} - -unsigned int CompilerPrivate::constIndex(InputValue *value, bool pointsToDropdownMenu, const std::string &selectedMenuItem) -{ - auto it = std::find(constValues.begin(), constValues.end(), value); - if (it != constValues.end()) - return it - constValues.begin(); - constValues.push_back(value); - constValueMenuInfo[value] = { pointsToDropdownMenu, selectedMenuItem }; - return constValues.size() - 1; -} - -void CompilerPrivate::substackEnd() -{ - auto parent = substackTree.back(); - switch (parent.second) { - case Compiler::SubstackType::Loop: - // Break the frame at the end of the loop so that other scripts can run within the frame - // This won't happen in "warp" scripts - if (!warp) - addInstruction(OP_BREAK_FRAME); - addInstruction(OP_LOOP_END); - break; - case Compiler::SubstackType::IfStatement: - if (parent.first.second) { - addInstruction(OP_ELSE); - block = parent.first.second; - substackTree[substackTree.size() - 1].first.second = nullptr; - return; - } else - addInstruction(OP_ENDIF); - break; - } - - auto parentBlock = parent.first.first; - - if (parentBlock) - block = parentBlock->next(); - else - block = nullptr; - - substackTree.pop_back(); - if (!block && !substackTree.empty()) - substackEnd(); -} diff --git a/src/engine/compiler_p.h b/src/engine/compiler_p.h deleted file mode 100644 index 5a396a7a..00000000 --- a/src/engine/compiler_p.h +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include - -namespace libscratchcpp -{ - -struct CompilerPrivate -{ - CompilerPrivate(IEngine *engine, Target *target); - CompilerPrivate(const CompilerPrivate &) = delete; - - void addInstruction(vm::Opcode opcode, std::initializer_list args = {}); - - unsigned int constIndex(InputValue *value, bool pointsToDropdownMenu = false, const std::string &selectedMenuItem = ""); - - void substackEnd(); - - IEngine *engine = nullptr; - Target *target = nullptr; - std::shared_ptr block; - std::vector, std::shared_ptr>, Compiler::SubstackType>> substackTree; - - bool initialized = false; - - std::vector bytecode; - std::vector hatPredicateBytecode; - std::vector constValues; - std::vector> customConstValues; - std::unordered_map> constValueMenuInfo; // input value, - std::vector variables; - std::vector lists; - std::vector procedures; - std::unordered_map> procedureArgs; - BlockPrototype *procedurePrototype = nullptr; - bool warp = false; - - std::unordered_set unsupportedBlocks; -}; - -} // namespace libscratchcpp diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index c0bd3173..f8584c70 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -7,12 +7,8 @@ #include #include #include -#ifdef USE_LLVM #include #include -#else -#include -#endif #include #include #include @@ -33,11 +29,7 @@ #include "timer.h" #include "clock.h" #include "audio/iaudioengine.h" -#ifdef USE_LLVM #include "dev/blocks/blocks.h" -#else -#include "blocks/blocks.h" -#endif #include "scratch/monitor_p.h" using namespace libscratchcpp; @@ -87,7 +79,6 @@ void Engine::clear() m_threads.clear(); m_threadsToStop.clear(); m_scripts.clear(); - m_functions.clear(); m_whenTouchingObjectHats.clear(); m_greenFlagHats.clear(); @@ -271,35 +262,18 @@ void Engine::compile() // Compile scripts to bytecode for (auto target : m_targets) { std::cout << "Compiling scripts in target " << target->name() << "..." << std::endl; -#ifdef USE_LLVM auto ctx = Compiler::createContext(this, target.get()); m_compilerContexts[target.get()] = ctx; Compiler compiler(ctx.get()); -#else - std::unordered_map procedureBytecodeMap; - Compiler compiler(this, target.get()); -#endif const auto &blocks = target->blocks(); + for (auto block : blocks) { if (block->topLevel() && !block->isTopLevelReporter() && !block->shadow()) { auto ext = blockExtension(block->opcode()); if (ext) { auto script = std::make_shared