diff --git a/include/scratchcpp/costume.h b/include/scratchcpp/costume.h index e81d26ce..1436a139 100644 --- a/include/scratchcpp/costume.h +++ b/include/scratchcpp/costume.h @@ -9,6 +9,7 @@ namespace libscratchcpp { +class Broadcast; class CostumePrivate; /*! \brief The Costume class represents a Scratch costume. */ @@ -27,6 +28,8 @@ class LIBSCRATCHCPP_EXPORT Costume : public Asset int rotationCenterY() const; void setRotationCenterY(int newRotationCenterY); + Broadcast *broadcast(); + private: spimpl::unique_impl_ptr impl; }; diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index 46e9fc7d..b497f1db 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -65,6 +65,9 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Starts the script of the broadcast with the given index. */ virtual void broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait = false) = 0; + /*! Starts the script of the given broadcast. */ + virtual void broadcastByPtr(Broadcast *broadcast, VirtualMachine *sourceScript, bool wait = false) = 0; + /*! Stops the given script. */ virtual void stopScript(VirtualMachine *vm) = 0; @@ -122,6 +125,9 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Returns true if there are any running script of the broadcast with the given index. */ virtual bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) = 0; + /*! Returns true if there are any running script of the given broadcast. */ + virtual bool broadcastByPtrRunning(Broadcast *broadcast, VirtualMachine *sourceScript) = 0; + /*! * Call this from a block implementation to force a "screen refresh". * \note This has no effect in "run without screen refresh" custom blocks. @@ -199,7 +205,7 @@ class LIBSCRATCHCPP_EXPORT IEngine virtual int findBroadcastById(const std::string &broadcastId) const = 0; /*! Registers the broadcast script. */ - virtual void addBroadcastScript(std::shared_ptr whenReceivedBlock, std::shared_ptr broadcast) = 0; + virtual void addBroadcastScript(std::shared_ptr whenReceivedBlock, Broadcast *broadcast) = 0; /* Registers the given "when I start as clone" script. */ virtual void addCloneInitScript(std::shared_ptr hatBlock) = 0; diff --git a/src/blocks/eventblocks.cpp b/src/blocks/eventblocks.cpp index f06192f2..1961ef13 100644 --- a/src/blocks/eventblocks.cpp +++ b/src/blocks/eventblocks.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "eventblocks.h" @@ -23,12 +25,14 @@ void EventBlocks::registerBlocks(IEngine *engine) engine->addCompileFunction(this, "event_broadcast", &compileBroadcast); engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait); engine->addCompileFunction(this, "event_whenbroadcastreceived", &compileWhenBroadcastReceived); + engine->addCompileFunction(this, "event_whenbackdropswitchesto", &compileWhenBackdropSwitchesTo); // Inputs engine->addInput(this, "BROADCAST_INPUT", BROADCAST_INPUT); // Fields engine->addField(this, "BROADCAST_OPTION", BROADCAST_OPTION); + engine->addField(this, "BACKDROP", BACKDROP); } void EventBlocks::compileBroadcast(Compiler *compiler) @@ -66,7 +70,18 @@ void EventBlocks::compileWhenBroadcastReceived(Compiler *compiler) { auto broadcast = std::static_pointer_cast(compiler->field(BROADCAST_OPTION)->valuePtr()); - compiler->engine()->addBroadcastScript(compiler->block(), broadcast); + compiler->engine()->addBroadcastScript(compiler->block(), broadcast.get()); +} + +void EventBlocks::compileWhenBackdropSwitchesTo(Compiler *compiler) +{ + if (Stage *stage = compiler->engine()->stage()) { + std::string backdropName = compiler->field(BACKDROP)->value().toString(); + int index = stage->findCostume(backdropName); + + if (index != -1) + compiler->engine()->addBroadcastScript(compiler->block(), stage->costumeAt(index)->broadcast()); + } } unsigned int EventBlocks::broadcast(VirtualMachine *vm) diff --git a/src/blocks/eventblocks.h b/src/blocks/eventblocks.h index f3b47b89..ca310d51 100644 --- a/src/blocks/eventblocks.h +++ b/src/blocks/eventblocks.h @@ -21,7 +21,8 @@ class EventBlocks : public IBlockSection enum Fields { - BROADCAST_OPTION + BROADCAST_OPTION, + BACKDROP }; std::string name() const override; @@ -31,6 +32,7 @@ class EventBlocks : public IBlockSection static void compileBroadcast(Compiler *compiler); static void compileBroadcastAndWait(Compiler *compiler); static void compileWhenBroadcastReceived(Compiler *compiler); + static void compileWhenBackdropSwitchesTo(Compiler *compiler); static unsigned int broadcast(VirtualMachine *vm); static unsigned int broadcastByIndex(VirtualMachine *vm); diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index 151036dc..754c3272 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -31,6 +31,7 @@ void LooksBlocks::registerBlocks(IEngine *engine) 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_costumenumbername", &compileCostumeNumberName); engine->addCompileFunction(this, "looks_backdropnumbername", &compileBackdropNumberName); @@ -157,6 +158,47 @@ void LooksBlocks::compileSwitchBackdropTo(Compiler *compiler) } } +void LooksBlocks::compileSwitchBackdropToAndWait(Compiler *compiler) +{ + Stage *stage = compiler->engine()->stage(); + + if (!stage) + return; + + Input *input = compiler->input(BACKDROP); + + if (input->type() != Input::Type::ObscuredShadow) { + assert(input->pointsToDropdownMenu()); + std::string value = input->selectedMenuItem(); + int index = stage->findCostume(value); + + if (index == -1) { + if (value == "next backdrop") + compiler->addFunctionCall(&nextBackdropAndWait); + else if (value == "previous backdrop") + compiler->addFunctionCall(&previousBackdropAndWait); + else if (value == "random backdrop") + compiler->addFunctionCall(&randomBackdropAndWait); + else { + Value v(value); + + if (v.type() == Value::Type::Integer) { + compiler->addConstValue(v.toLong() - 1); + compiler->addFunctionCall(&switchBackdropToByIndexAndWait); + } + } + } else { + compiler->addConstValue(index); + compiler->addFunctionCall(&switchBackdropToByIndexAndWait); + } + } else { + compiler->addInput(input); + compiler->addFunctionCall(&switchBackdropToAndWait); + } + + compiler->addFunctionCall(&checkBackdropScripts); +} + void LooksBlocks::compileNextBackdrop(Compiler *compiler) { compiler->addFunctionCall(&nextBackdrop); @@ -308,20 +350,26 @@ unsigned int LooksBlocks::previousCostume(VirtualMachine *vm) return 0; } -unsigned int LooksBlocks::switchBackdropToByIndex(VirtualMachine *vm) +void LooksBlocks::startBackdropScripts(VirtualMachine *vm, bool wait) +{ + if (Stage *stage = vm->engine()->stage()) { + if (stage->costumes().size() > 0) + vm->engine()->broadcastByPtr(stage->costumeAt(stage->currentCostume() - 1)->broadcast(), vm, wait); + } +} + +void LooksBlocks::switchBackdropToByIndexImpl(VirtualMachine *vm) { if (Stage *stage = vm->engine()->stage()) setCostumeByIndex(stage, vm->getInput(0, 1)->toLong()); - - return 1; } -unsigned int LooksBlocks::switchBackdropTo(VirtualMachine *vm) +void LooksBlocks::switchBackdropToImpl(VirtualMachine *vm) { Stage *stage = vm->engine()->stage(); if (!stage) - return 1; + return; const Value *name = vm->getInput(0, 1); std::string nameStr = name->toString(); @@ -329,38 +377,32 @@ unsigned int LooksBlocks::switchBackdropTo(VirtualMachine *vm) if (index == -1) { if (nameStr == "next backdrop") - nextBackdrop(vm); + nextBackdropImpl(vm); else if (nameStr == "previous backdrop") - previousBackdrop(vm); + previousBackdropImpl(vm); else if (nameStr == "random backdrop") { - randomBackdrop(vm); + randomBackdropImpl(vm); } else { if (name->type() == Value::Type::Integer) setCostumeByIndex(stage, name->toLong() - 1); } } else setCostumeByIndex(stage, index); - - return 1; } -unsigned int LooksBlocks::nextBackdrop(VirtualMachine *vm) +void LooksBlocks::nextBackdropImpl(VirtualMachine *vm) { if (Stage *stage = vm->engine()->stage()) setCostumeByIndex(stage, stage->currentCostume()); - - return 0; } -unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm) +void LooksBlocks::previousBackdropImpl(VirtualMachine *vm) { if (Stage *stage = vm->engine()->stage()) setCostumeByIndex(stage, stage->currentCostume() - 2); - - return 0; } -unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm) +void LooksBlocks::randomBackdropImpl(VirtualMachine *vm) { if (!rng) rng = RandomGenerator::instance().get(); @@ -371,6 +413,94 @@ unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm) if (count > 0) stage->setCurrentCostume(rng->randint(1, count)); } +} + +unsigned int LooksBlocks::switchBackdropToByIndex(VirtualMachine *vm) +{ + switchBackdropToByIndexImpl(vm); + startBackdropScripts(vm, false); + + return 1; +} + +unsigned int LooksBlocks::switchBackdropTo(VirtualMachine *vm) +{ + switchBackdropToImpl(vm); + startBackdropScripts(vm, false); + + return 1; +} + +unsigned int LooksBlocks::switchBackdropToByIndexAndWait(VirtualMachine *vm) +{ + switchBackdropToByIndexImpl(vm); + startBackdropScripts(vm, true); + + return 1; +} + +unsigned int LooksBlocks::switchBackdropToAndWait(VirtualMachine *vm) +{ + switchBackdropToImpl(vm); + startBackdropScripts(vm, true); + + return 1; +} + +unsigned int LooksBlocks::nextBackdrop(VirtualMachine *vm) +{ + nextBackdropImpl(vm); + startBackdropScripts(vm, false); + + return 0; +} + +unsigned int LooksBlocks::nextBackdropAndWait(VirtualMachine *vm) +{ + nextBackdropImpl(vm); + startBackdropScripts(vm, true); + + return 0; +} + +unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm) +{ + previousBackdropImpl(vm); + startBackdropScripts(vm, false); + + return 0; +} + +unsigned int LooksBlocks::previousBackdropAndWait(VirtualMachine *vm) +{ + previousBackdropImpl(vm); + startBackdropScripts(vm, true); + + return 0; +} + +unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm) +{ + randomBackdropImpl(vm); + startBackdropScripts(vm, false); + + return 0; +} + +unsigned int LooksBlocks::randomBackdropAndWait(VirtualMachine *vm) +{ + randomBackdropImpl(vm); + startBackdropScripts(vm, true); + + return 0; +} + +unsigned int LooksBlocks::checkBackdropScripts(VirtualMachine *vm) +{ + if (Stage *stage = vm->engine()->stage()) { + if ((stage->costumes().size() > 0) && vm->engine()->broadcastByPtrRunning(stage->costumeAt(stage->currentCostume() - 1)->broadcast(), vm)) + vm->stop(true, true, true); + } return 0; } diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index 8f78453d..cefe7e9b 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -8,6 +8,8 @@ namespace libscratchcpp { class Target; +class Stage; +class Value; class IRandomGenerator; /*! \brief The LooksBlocks class contains the implementation of looks blocks. */ @@ -45,6 +47,7 @@ class LooksBlocks : public IBlockSection 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 compileCostumeNumberName(Compiler *compiler); static void compileBackdropNumberName(Compiler *compiler); @@ -61,11 +64,24 @@ class LooksBlocks : public IBlockSection static unsigned int nextCostume(VirtualMachine *vm); static unsigned int previousCostume(VirtualMachine *vm); + static void startBackdropScripts(VirtualMachine *vm, bool wait); + static void switchBackdropToByIndexImpl(VirtualMachine *vm); + static void switchBackdropToImpl(VirtualMachine *vm); + static void nextBackdropImpl(VirtualMachine *vm); + static void previousBackdropImpl(VirtualMachine *vm); + static void randomBackdropImpl(VirtualMachine *vm); + static unsigned int switchBackdropToByIndex(VirtualMachine *vm); static unsigned int switchBackdropTo(VirtualMachine *vm); + static unsigned int switchBackdropToByIndexAndWait(VirtualMachine *vm); + static unsigned int switchBackdropToAndWait(VirtualMachine *vm); static unsigned int nextBackdrop(VirtualMachine *vm); + static unsigned int nextBackdropAndWait(VirtualMachine *vm); static unsigned int previousBackdrop(VirtualMachine *vm); + static unsigned int previousBackdropAndWait(VirtualMachine *vm); static unsigned int randomBackdrop(VirtualMachine *vm); + static unsigned int randomBackdropAndWait(VirtualMachine *vm); + static unsigned int checkBackdropScripts(VirtualMachine *vm); static unsigned int costumeNumber(VirtualMachine *vm); static unsigned int costumeName(VirtualMachine *vm); diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index e4190948..4fb00a68 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -207,10 +208,18 @@ void Engine::startScript(std::shared_ptr topLevelBlock, std::shared_ptr= m_broadcasts.size()) + return; + + broadcastByPtr(m_broadcasts[index].get(), sourceScript, wait); +} + +void Engine::broadcastByPtr(Broadcast *broadcast, VirtualMachine *sourceScript, bool wait) { bool previousSkipFrame = m_skipFrame; skipFrame(); - const std::vector