Skip to content

Commit e82ef6c

Browse files
authored
Merge pull request #480 from scratchcpp/ask_and_wait_block
Implement the ask and wait and answer blocks
2 parents 548df99 + 069f2aa commit e82ef6c

File tree

10 files changed

+407
-2
lines changed

10 files changed

+407
-2
lines changed

include/scratchcpp/iengine.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,18 @@ class LIBSCRATCHCPP_EXPORT IEngine
338338
/*! Sets the function which is called when a monitor is removed. */
339339
virtual void setRemoveMonitorHandler(const std::function<void(Monitor *, IMonitorHandler *)> &handler) = 0;
340340

341+
/*! Returns the function which is called when a question is asked, for example using the 'ask and wait' block. */
342+
virtual const std::function<void(const std::string &)> &questionAsked() const = 0;
343+
344+
/*! Sets the function which is called when a question is asked, for example using the 'ask and wait' block. */
345+
virtual void setQuestionAsked(const std::function<void(const std::string &)> &f) = 0;
346+
347+
/*! Returns the function which should be called when a question is answered. */
348+
virtual const std::function<void(const std::string &)> &questionAnswered() const = 0;
349+
350+
/*! Sets the function which should be called when a question is answered. */
351+
virtual void setQuestionAnswered(const std::function<void(const std::string &)> &f) = 0;
352+
341353
/*! Returns the list of extension names. */
342354
virtual const std::vector<std::string> &extensions() const = 0;
343355

src/blocks/sensingblocks.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ void SensingBlocks::registerBlocks(IEngine *engine)
2828
{
2929
// Blocks
3030
engine->addCompileFunction(this, "sensing_distanceto", &compileDistanceTo);
31+
engine->addCompileFunction(this, "sensing_askandwait", &compileAskAndWait);
32+
engine->addCompileFunction(this, "sensing_answer", &compileAnswer);
3133
engine->addCompileFunction(this, "sensing_keypressed", &compileKeyPressed);
3234
engine->addCompileFunction(this, "sensing_mousedown", &compileMouseDown);
3335
engine->addCompileFunction(this, "sensing_mousex", &compileMouseX);
@@ -77,6 +79,9 @@ void SensingBlocks::registerBlocks(IEngine *engine)
7779
engine->addFieldValue(this, "background #", BackdropNumber); // Scratch 1.4 support
7880
engine->addFieldValue(this, "backdrop #", BackdropNumber);
7981
engine->addFieldValue(this, "backdrop name", BackdropName);
82+
83+
// Callbacks
84+
engine->setQuestionAnswered(&onAnswer);
8085
}
8186

8287
void SensingBlocks::compileDistanceTo(Compiler *compiler)
@@ -100,6 +105,17 @@ void SensingBlocks::compileDistanceTo(Compiler *compiler)
100105
}
101106
}
102107

108+
void SensingBlocks::compileAskAndWait(Compiler *compiler)
109+
{
110+
compiler->addInput(QUESTION);
111+
compiler->addFunctionCall(&askAndWait);
112+
}
113+
114+
void SensingBlocks::compileAnswer(Compiler *compiler)
115+
{
116+
compiler->addFunctionCall(&answer);
117+
}
118+
103119
void SensingBlocks::compileKeyPressed(Compiler *compiler)
104120
{
105121
compiler->addInput(KEY_OPTION);
@@ -488,6 +504,45 @@ unsigned int SensingBlocks::distanceToMousePointer(VirtualMachine *vm)
488504
return 0;
489505
}
490506

507+
void SensingBlocks::onAnswer(const std::string &answer)
508+
{
509+
// https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L99-L115
510+
m_answer = answer;
511+
512+
if (!m_questionList.empty()) {
513+
Question *question = m_questionList.front().get();
514+
VirtualMachine *vm = question->vm;
515+
assert(vm);
516+
assert(vm->target());
517+
518+
// If the target was visible when asked, hide the say bubble unless the target was the stage
519+
if (question->wasVisible && !question->wasStage)
520+
vm->target()->setBubbleText("");
521+
522+
m_questionList.erase(m_questionList.begin());
523+
vm->resolvePromise();
524+
askNextQuestion();
525+
}
526+
}
527+
528+
unsigned int SensingBlocks::askAndWait(VirtualMachine *vm)
529+
{
530+
const bool isQuestionAsked = !m_questionList.empty();
531+
enqueueAsk(vm->getInput(0, 1)->toString(), vm);
532+
533+
if (!isQuestionAsked)
534+
askNextQuestion();
535+
536+
vm->promise();
537+
return 1;
538+
}
539+
540+
unsigned int SensingBlocks::answer(VirtualMachine *vm)
541+
{
542+
vm->addReturnValue(m_answer);
543+
return 0;
544+
}
545+
491546
unsigned int SensingBlocks::timer(VirtualMachine *vm)
492547
{
493548
vm->addReturnValue(vm->engine()->timer()->value());
@@ -822,3 +877,44 @@ unsigned int SensingBlocks::daysSince2000(VirtualMachine *vm)
822877

823878
return 0;
824879
}
880+
881+
void SensingBlocks::enqueueAsk(const std::string &question, VirtualMachine *vm)
882+
{
883+
// https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L117-L119
884+
assert(vm);
885+
Target *target = vm->target();
886+
assert(target);
887+
bool visible = true;
888+
bool isStage = target->isStage();
889+
890+
if (!isStage) {
891+
Sprite *sprite = static_cast<Sprite *>(target);
892+
visible = sprite->visible();
893+
}
894+
895+
m_questionList.push_back(std::make_unique<Question>(question, vm, visible, isStage));
896+
}
897+
898+
void SensingBlocks::askNextQuestion()
899+
{
900+
// https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L121-L133
901+
if (m_questionList.empty())
902+
return;
903+
904+
Question *question = m_questionList.front().get();
905+
Target *target = question->vm->target();
906+
auto ask = question->vm->engine()->questionAsked();
907+
908+
// If the target is visible, emit a blank question and show
909+
// a bubble unless the target was the stage
910+
if (question->wasVisible && !question->wasStage) {
911+
target->setBubbleType(Target::BubbleType::Say);
912+
target->setBubbleText(question->question);
913+
914+
if (ask)
915+
ask("");
916+
} else {
917+
if (ask)
918+
ask(question->question);
919+
}
920+
}

src/blocks/sensingblocks.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@
33
#pragma once
44

55
#include <scratchcpp/iblocksection.h>
6+
#include <scratchcpp/value.h>
7+
#include <vector>
8+
69
#include "../engine/internal/clock.h"
710

811
namespace libscratchcpp
912
{
1013

14+
class Target;
15+
1116
/*! \brief The SensingBlocks class contains the implementation of sensing blocks. */
1217
class SensingBlocks : public IBlockSection
1318
{
1419
public:
1520
enum Inputs
1621
{
1722
DISTANCETOMENU,
23+
QUESTION,
1824
KEY_OPTION,
1925
OBJECT
2026
};
@@ -53,6 +59,8 @@ class SensingBlocks : public IBlockSection
5359
void registerBlocks(IEngine *engine) override;
5460

5561
static void compileDistanceTo(Compiler *compiler);
62+
static void compileAskAndWait(Compiler *compiler);
63+
static void compileAnswer(Compiler *compiler);
5664
static void compileKeyPressed(Compiler *compiler);
5765
static void compileMouseDown(Compiler *compiler);
5866
static void compileMouseX(Compiler *compiler);
@@ -83,6 +91,10 @@ class SensingBlocks : public IBlockSection
8391
static unsigned int distanceToByIndex(VirtualMachine *vm);
8492
static unsigned int distanceToMousePointer(VirtualMachine *vm);
8593

94+
static void onAnswer(const std::string &answer);
95+
static unsigned int askAndWait(VirtualMachine *vm);
96+
static unsigned int answer(VirtualMachine *vm);
97+
8698
static unsigned int timer(VirtualMachine *vm);
8799
static unsigned int resetTimer(VirtualMachine *vm);
88100

@@ -116,6 +128,29 @@ class SensingBlocks : public IBlockSection
116128
static unsigned int daysSince2000(VirtualMachine *vm);
117129

118130
static IClock *clock;
131+
132+
private:
133+
struct Question
134+
{
135+
Question(const std::string &question, VirtualMachine *vm, bool wasVisible, bool wasStage) :
136+
question(question),
137+
vm(vm),
138+
wasVisible(wasVisible),
139+
wasStage(wasStage)
140+
{
141+
}
142+
143+
std::string question;
144+
VirtualMachine *vm = nullptr;
145+
bool wasVisible = false;
146+
bool wasStage = false;
147+
};
148+
149+
static void enqueueAsk(const std::string &question, VirtualMachine *vm);
150+
static void askNextQuestion();
151+
152+
static inline std::vector<std::unique_ptr<Question>> m_questionList;
153+
static inline Value m_answer;
119154
};
120155

121156
} // namespace libscratchcpp

src/engine/internal/engine.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,26 @@ void Engine::setRemoveMonitorHandler(const std::function<void(Monitor *, IMonito
11701170
m_removeMonitorHandler = handler;
11711171
}
11721172

1173+
const std::function<void(const std::string &)> &Engine::questionAsked() const
1174+
{
1175+
return m_questionAsked;
1176+
}
1177+
1178+
void Engine::setQuestionAsked(const std::function<void(const std::string &)> &f)
1179+
{
1180+
m_questionAsked = f;
1181+
}
1182+
1183+
const std::function<void(const std::string &)> &Engine::questionAnswered() const
1184+
{
1185+
return m_questionAnswered;
1186+
}
1187+
1188+
void Engine::setQuestionAnswered(const std::function<void(const std::string &)> &f)
1189+
{
1190+
m_questionAnswered = f;
1191+
}
1192+
11731193
const std::vector<std::string> &Engine::extensions() const
11741194
{
11751195
return m_extensions;

src/engine/internal/engine.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ class Engine : public IEngine
143143
void setAddMonitorHandler(const std::function<void(Monitor *)> &handler) override;
144144
void setRemoveMonitorHandler(const std::function<void(Monitor *, IMonitorHandler *)> &handler) override;
145145

146+
const std::function<void(const std::string &)> &questionAsked() const override;
147+
void setQuestionAsked(const std::function<void(const std::string &)> &f) override;
148+
149+
const std::function<void(const std::string &)> &questionAnswered() const override;
150+
void setQuestionAnswered(const std::function<void(const std::string &)> &f) override;
151+
146152
const std::vector<std::string> &extensions() const override;
147153
void setExtensions(const std::vector<std::string> &newExtensions) override;
148154

@@ -255,6 +261,8 @@ class Engine : public IEngine
255261

256262
std::function<void(Monitor *)> m_addMonitorHandler = nullptr;
257263
std::function<void(Monitor *, IMonitorHandler *)> m_removeMonitorHandler = nullptr;
264+
std::function<void(const std::string &)> m_questionAsked = nullptr;
265+
std::function<void(const std::string &)> m_questionAnswered = nullptr;
258266
};
259267

260268
} // namespace libscratchcpp

src/scratch/sprite.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ void Sprite::setBubbleType(BubbleType type)
441441
/*! Overrides Target#setBubbleText(). */
442442
void Sprite::setBubbleText(const std::string &text)
443443
{
444-
Target::setBubbleText(text);
444+
Target::setBubbleText(impl->visible ? text : "");
445445

446446
if (impl->visible && !text.empty()) {
447447
IEngine *eng = engine();
@@ -451,7 +451,7 @@ void Sprite::setBubbleText(const std::string &text)
451451
}
452452

453453
if (impl->iface)
454-
impl->iface->onBubbleTextChanged(text);
454+
impl->iface->onBubbleTextChanged(impl->visible ? text : "");
455455
}
456456

457457
Target *Sprite::dataSource() const

0 commit comments

Comments
 (0)