@@ -38,6 +38,11 @@ const std::unordered_map<Engine::HatType, bool> Engine::m_hatRestartExistingThre
3838 { HatType::CloneInit, false }, { HatType::KeyPressed, false }, { HatType::TargetClicked, true }
3939};
4040
41+ const std::unordered_map<Engine::HatType, bool > Engine::m_hatEdgeActivated = {
42+ { HatType::GreenFlag, false }, { HatType::BroadcastReceived, false }, { HatType::BackdropChanged, false },
43+ { HatType::CloneInit, false }, { HatType::KeyPressed, false }, { HatType::TargetClicked, false }
44+ };
45+
4146Engine::Engine () :
4247 m_defaultTimer(std::make_unique<Timer>()),
4348 m_timer(m_defaultTimer.get()),
@@ -79,6 +84,7 @@ void Engine::clear()
7984 m_whenKeyPressedHats.clear ();
8085
8186 m_scriptHatFields.clear ();
87+ m_edgeActivatedHatValues.clear ();
8288
8389 m_running = false ;
8490}
@@ -93,8 +99,11 @@ void Engine::resolveIds()
9399 auto container = blockSectionContainer (block->opcode ());
94100 block->setNext (getBlock (block->nextId ()));
95101 block->setParent (getBlock (block->parentId ()));
96- if (container)
102+
103+ if (container) {
97104 block->setCompileFunction (container->resolveBlockCompileFunc (block->opcode ()));
105+ block->setHatPredicateCompileFunction (container->resolveHatPredicateCompileFunc (block->opcode ()));
106+ }
98107
99108 const auto &inputs = block->inputs ();
100109 for (const auto &input : inputs) {
@@ -215,6 +224,8 @@ void Engine::compile()
215224 compiler.compile (block);
216225
217226 script->setBytecode (compiler.bytecode ());
227+ script->setHatPredicateBytecode (compiler.hatPredicateBytecode ());
228+
218229 if (block->opcode () == " procedures_definition" ) {
219230 auto b = block->inputAt (block->findInput (" custom_block" ))->valueBlock ();
220231 procedureBytecodeMap[b->mutationPrototype ()->procCode ()] = script->bytecode ();
@@ -295,6 +306,7 @@ void Engine::start()
295306
296307 m_eventLoopMutex.lock ();
297308 m_timer->reset ();
309+ m_edgeActivatedHatValues.clear ();
298310 m_running = true ;
299311
300312 // Start "when green flag clicked" scripts
@@ -432,6 +444,30 @@ void Engine::step()
432444 // Clean up threads that were told to stop during or since the last step
433445 m_threads.erase (std::remove_if (m_threads.begin (), m_threads.end (), [](std::shared_ptr<VirtualMachine> thread) { return thread->atEnd (); }), m_threads.end ());
434446
447+ // Find all edge-activated hats, and add them to threads to be evaluated
448+ for (auto const &[hatType, edgeActivated] : m_hatEdgeActivated) {
449+ if (edgeActivated) {
450+ auto newThreads = startHats (hatType, {}, nullptr );
451+
452+ // Process edge-triggered hats (must happen here because of Scratch 2 compatibility)
453+ // Processing the hats means running their predicates (if they didn't change their return value from false to true, remove the threads)
454+ for (auto thread : newThreads) {
455+ bool oldValue = false ;
456+ auto it = m_edgeActivatedHatValues.find (hatType);
457+
458+ if (it != m_edgeActivatedHatValues.cend ())
459+ oldValue = it->second ;
460+
461+ bool newValue = thread->script ()->runHatPredicate ();
462+ bool edgeWasActivated = !oldValue && newValue; // changed from false true
463+ m_edgeActivatedHatValues[hatType] = newValue;
464+
465+ if (!edgeWasActivated)
466+ stopThread (thread.get ());
467+ }
468+ }
469+ }
470+
435471 m_redrawRequested = false ;
436472
437473 // Step threads
@@ -856,6 +892,14 @@ void Engine::addCompileFunction(IBlockSection *section, const std::string &opcod
856892 container->addCompileFunction (opcode, f);
857893}
858894
895+ void Engine::addHatPredicateCompileFunction (IBlockSection *section, const std::string &opcode, HatPredicateCompileFunc f)
896+ {
897+ auto container = blockSectionContainer (section);
898+
899+ if (container)
900+ container->addHatPredicateCompileFunction (opcode, f);
901+ }
902+
859903void Engine::addMonitorNameFunction (IBlockSection *section, const std::string &opcode, MonitorNameFunc f)
860904{
861905 auto container = blockSectionContainer (section);
@@ -1718,11 +1762,5 @@ Engine::startHats(HatType hatType, const std::unordered_map<HatField, std::varia
17181762 },
17191763 optTarget);
17201764
1721- // Run edge-triggered hats (for compatibility with Scratch 2)
1722- // TODO: Find out what "edge-triggered" hats are and execute them
1723- // Uncommenting this would cause infinite recursion in some cases, so let's keep it commented
1724- /* for (auto thread : newThreads)
1725- thread->run();*/
1726-
17271765 return newThreads;
17281766}
0 commit comments