Skip to content

Commit 5f3553d

Browse files
committed
Implement sensing_touchingobject block
1 parent 90311da commit 5f3553d

File tree

3 files changed

+266
-0
lines changed

3 files changed

+266
-0
lines changed

src/blocks/sensingblocks.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ std::string SensingBlocks::name() const
3030
void SensingBlocks::registerBlocks(IEngine *engine)
3131
{
3232
// Blocks
33+
engine->addCompileFunction(this, "sensing_touchingobject", &compileTouchingObject);
3334
engine->addCompileFunction(this, "sensing_distanceto", &compileDistanceTo);
3435
engine->addCompileFunction(this, "sensing_askandwait", &compileAskAndWait);
3536
engine->addCompileFunction(this, "sensing_answer", &compileAnswer);
@@ -56,6 +57,7 @@ void SensingBlocks::registerBlocks(IEngine *engine)
5657
engine->addMonitorNameFunction(this, "sensing_dayssince2000", &daysSince2000MonitorName);
5758

5859
// Inputs
60+
engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU);
5961
engine->addInput(this, "DISTANCETOMENU", DISTANCETOMENU);
6062
engine->addInput(this, "QUESTION", QUESTION);
6163
engine->addInput(this, "KEY_OPTION", KEY_OPTION);
@@ -91,6 +93,29 @@ void SensingBlocks::registerBlocks(IEngine *engine)
9193
engine->questionAnswered().connect(&onAnswer);
9294
}
9395

96+
void SensingBlocks::compileTouchingObject(Compiler *compiler)
97+
{
98+
Input *input = compiler->input(TOUCHINGOBJECTMENU);
99+
100+
if (input->type() != Input::Type::ObscuredShadow) {
101+
assert(input->pointsToDropdownMenu());
102+
std::string value = input->selectedMenuItem();
103+
104+
if (value == "_mouse_")
105+
compiler->addFunctionCall(&touchingMousePointer);
106+
else if (value == "_edge_")
107+
compiler->addFunctionCall(&touchingEdge);
108+
else {
109+
int index = compiler->engine()->findTarget(value);
110+
compiler->addConstValue(index);
111+
compiler->addFunctionCall(&touchingObjectByIndex);
112+
}
113+
} else {
114+
compiler->addInput(input);
115+
compiler->addFunctionCall(&touchingObject);
116+
}
117+
}
118+
94119
void SensingBlocks::compileDistanceTo(Compiler *compiler)
95120
{
96121
Input *input = compiler->input(DISTANCETOMENU);
@@ -435,6 +460,41 @@ const std::string &SensingBlocks::daysSince2000MonitorName(Block *block)
435460
return name;
436461
}
437462

463+
unsigned int SensingBlocks::touchingObject(VirtualMachine *vm)
464+
{
465+
std::string value = vm->getInput(0, 1)->toString();
466+
467+
if (value == "_mouse_")
468+
vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1);
469+
else if (value == "_edge_")
470+
vm->replaceReturnValue(vm->target()->touchingEdge(), 1);
471+
else {
472+
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value));
473+
vm->replaceReturnValue(touchingObjectCommon(vm->target(), target), 1);
474+
}
475+
476+
return 0;
477+
}
478+
479+
unsigned int SensingBlocks::touchingObjectByIndex(VirtualMachine *vm)
480+
{
481+
Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt());
482+
vm->replaceReturnValue(touchingObjectCommon(vm->target(), target), 1);
483+
return 0;
484+
}
485+
486+
unsigned int SensingBlocks::touchingMousePointer(VirtualMachine *vm)
487+
{
488+
vm->addReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()));
489+
return 0;
490+
}
491+
492+
unsigned int SensingBlocks::touchingEdge(VirtualMachine *vm)
493+
{
494+
vm->addReturnValue(vm->target()->touchingEdge());
495+
return 0;
496+
}
497+
438498
unsigned int SensingBlocks::keyPressed(VirtualMachine *vm)
439499
{
440500
vm->replaceReturnValue(vm->engine()->keyPressed(vm->getInput(0, 1)->toString()), 1);
@@ -921,6 +981,14 @@ unsigned int SensingBlocks::daysSince2000(VirtualMachine *vm)
921981
return 0;
922982
}
923983

984+
bool SensingBlocks::touchingObjectCommon(Target *source, Target *target)
985+
{
986+
if (source && target && !target->isStage())
987+
return source->touchingSprite(static_cast<Sprite *>(target));
988+
989+
return false;
990+
}
991+
924992
void SensingBlocks::enqueueAsk(const std::string &question, VirtualMachine *vm)
925993
{
926994
// https://github.com/scratchfoundation/scratch-vm/blob/6055823f203a696165084b873e661713806583ec/src/blocks/scratch3_sensing.js#L117-L119

src/blocks/sensingblocks.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class SensingBlocks : public IBlockSection
2020
public:
2121
enum Inputs
2222
{
23+
TOUCHINGOBJECTMENU,
2324
DISTANCETOMENU,
2425
QUESTION,
2526
KEY_OPTION,
@@ -59,6 +60,7 @@ class SensingBlocks : public IBlockSection
5960

6061
void registerBlocks(IEngine *engine) override;
6162

63+
static void compileTouchingObject(Compiler *compiler);
6264
static void compileDistanceTo(Compiler *compiler);
6365
static void compileAskAndWait(Compiler *compiler);
6466
static void compileAnswer(Compiler *compiler);
@@ -83,6 +85,11 @@ class SensingBlocks : public IBlockSection
8385
static const std::string &currentMonitorName(Block *block);
8486
static const std::string &daysSince2000MonitorName(Block *block);
8587

88+
static unsigned int touchingObject(VirtualMachine *vm);
89+
static unsigned int touchingObjectByIndex(VirtualMachine *vm);
90+
static unsigned int touchingMousePointer(VirtualMachine *vm);
91+
static unsigned int touchingEdge(VirtualMachine *vm);
92+
8693
static unsigned int keyPressed(VirtualMachine *vm);
8794
static unsigned int mouseDown(VirtualMachine *vm);
8895
static unsigned int mouseX(VirtualMachine *vm);
@@ -154,6 +161,8 @@ class SensingBlocks : public IBlockSection
154161
bool wasStage = false;
155162
};
156163

164+
static bool touchingObjectCommon(Target *source, Target *target);
165+
157166
static void enqueueAsk(const std::string &question, VirtualMachine *vm);
158167
static void askNextQuestion();
159168

test/blocks/sensing_blocks_test.cpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <clockmock.h>
1212
#include <audioinputmock.h>
1313
#include <audioloudnessmock.h>
14+
#include <targetmock.h>
1415

1516
#include "../common.h"
1617
#include "blocks/sensingblocks.h"
@@ -115,6 +116,7 @@ TEST_F(SensingBlocksTest, CategoryVisible)
115116
TEST_F(SensingBlocksTest, RegisterBlocks)
116117
{
117118
// Blocks
119+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sensing_touchingobject", &SensingBlocks::compileTouchingObject));
118120
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sensing_distanceto", &SensingBlocks::compileDistanceTo));
119121
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sensing_askandwait", &SensingBlocks::compileAskAndWait));
120122
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sensing_answer", &SensingBlocks::compileAnswer));
@@ -141,6 +143,7 @@ TEST_F(SensingBlocksTest, RegisterBlocks)
141143
EXPECT_CALL(m_engineMock, addMonitorNameFunction(m_section.get(), "sensing_dayssince2000", &SensingBlocks::daysSince2000MonitorName));
142144

143145
// Inputs
146+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "TOUCHINGOBJECTMENU", SensingBlocks::TOUCHINGOBJECTMENU));
144147
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "DISTANCETOMENU", SensingBlocks::DISTANCETOMENU));
145148
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "QUESTION", SensingBlocks::QUESTION));
146149
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "KEY_OPTION", SensingBlocks::KEY_OPTION));
@@ -182,6 +185,192 @@ TEST_F(SensingBlocksTest, RegisterBlocks)
182185
ASSERT_EQ(questionAnswered.disconnect(&SensingBlocks::onAnswer), 1);
183186
}
184187

188+
TEST_F(SensingBlocksTest, TouchingObject)
189+
{
190+
Compiler compiler(&m_engineMock);
191+
192+
// touching (Sprite2)
193+
auto block1 = std::make_shared<Block>("a", "sensing_touchingobject");
194+
addDropdownInput(block1, "TOUCHINGOBJECTMENU", SensingBlocks::TOUCHINGOBJECTMENU, "Sprite2");
195+
196+
// touching (mouse-pointer)
197+
auto block2 = std::make_shared<Block>("b", "sensing_touchingobject");
198+
addDropdownInput(block2, "TOUCHINGOBJECTMENU", SensingBlocks::TOUCHINGOBJECTMENU, "_mouse_");
199+
200+
// touching (edge)
201+
auto block3 = std::make_shared<Block>("c", "sensing_touchingobject");
202+
addDropdownInput(block3, "TOUCHINGOBJECTMENU", SensingBlocks::TOUCHINGOBJECTMENU, "_edge_");
203+
204+
// touching (null block)
205+
auto block4 = std::make_shared<Block>("d", "sensing_distanceto");
206+
addDropdownInput(block4, "TOUCHINGOBJECTMENU", SensingBlocks::TOUCHINGOBJECTMENU, "", createNullBlock("e"));
207+
208+
compiler.init();
209+
210+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(5));
211+
EXPECT_CALL(m_engineMock, functionIndex(&SensingBlocks::touchingObjectByIndex)).WillOnce(Return(0));
212+
compiler.setBlock(block1);
213+
SensingBlocks::compileTouchingObject(&compiler);
214+
215+
EXPECT_CALL(m_engineMock, functionIndex(&SensingBlocks::touchingMousePointer)).WillOnce(Return(1));
216+
compiler.setBlock(block2);
217+
SensingBlocks::compileTouchingObject(&compiler);
218+
219+
EXPECT_CALL(m_engineMock, functionIndex(&SensingBlocks::touchingEdge)).WillOnce(Return(2));
220+
compiler.setBlock(block3);
221+
SensingBlocks::compileTouchingObject(&compiler);
222+
223+
EXPECT_CALL(m_engineMock, functionIndex(&SensingBlocks::touchingObject)).WillOnce(Return(3));
224+
compiler.setBlock(block4);
225+
SensingBlocks::compileTouchingObject(&compiler);
226+
227+
compiler.end();
228+
229+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 3, vm::OP_HALT }));
230+
ASSERT_EQ(compiler.constValues().size(), 1);
231+
ASSERT_EQ(compiler.constValues()[0].toDouble(), 5);
232+
}
233+
234+
TEST_F(SensingBlocksTest, TouchingObjectImpl)
235+
{
236+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };
237+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT };
238+
static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT };
239+
static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_EXEC, 0, vm::OP_HALT };
240+
static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 4, vm::OP_EXEC, 1, vm::OP_HALT };
241+
static unsigned int bytecode6[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 1, vm::OP_HALT };
242+
static unsigned int bytecode7[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 1, vm::OP_HALT };
243+
static unsigned int bytecode8[] = { vm::OP_START, vm::OP_EXEC, 2, vm::OP_HALT };
244+
static unsigned int bytecode9[] = { vm::OP_START, vm::OP_EXEC, 3, vm::OP_HALT };
245+
static BlockFunc functions[] = { &SensingBlocks::touchingObject, &SensingBlocks::touchingObjectByIndex, &SensingBlocks::touchingMousePointer, &SensingBlocks::touchingEdge };
246+
static Value constValues[] = { "Sprite2", "_mouse_", "_edge_", "", 1, -1, 2 };
247+
248+
TargetMock target;
249+
target.setEngine(&m_engineMock);
250+
Sprite sprite;
251+
VirtualMachine vm(&target, &m_engineMock, nullptr);
252+
vm.setFunctions(functions);
253+
vm.setConstValues(constValues);
254+
255+
// touching "Sprite2"
256+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(3));
257+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&sprite));
258+
EXPECT_CALL(target, touchingClones).WillOnce(Return(false));
259+
vm.setBytecode(bytecode1);
260+
vm.run();
261+
262+
ASSERT_EQ(vm.registerCount(), 1);
263+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
264+
265+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(3));
266+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&sprite));
267+
EXPECT_CALL(target, touchingClones).WillOnce(Return(true));
268+
vm.reset();
269+
vm.run();
270+
271+
ASSERT_EQ(vm.registerCount(), 1);
272+
ASSERT_TRUE(vm.getInput(0, 1)->toBool());
273+
274+
// touching "_mouse_"
275+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(24.5));
276+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(-16.04));
277+
EXPECT_CALL(target, touchingPoint(24.5, -16.04)).WillOnce(Return(true));
278+
vm.setBytecode(bytecode2);
279+
vm.reset();
280+
vm.run();
281+
282+
ASSERT_EQ(vm.registerCount(), 1);
283+
ASSERT_TRUE(vm.getInput(0, 1)->toBool());
284+
285+
// touching "_edge_"
286+
EXPECT_CALL(m_engineMock, stageWidth()).WillOnce(Return(0));
287+
EXPECT_CALL(m_engineMock, stageHeight()).WillOnce(Return(0));
288+
EXPECT_CALL(target, boundingRect()).WillOnce(Return(Rect(-5, 5, 5, -5)));
289+
vm.setBytecode(bytecode3);
290+
vm.reset();
291+
vm.run();
292+
293+
ASSERT_EQ(vm.registerCount(), 1);
294+
ASSERT_TRUE(vm.getInput(0, 1)->toBool());
295+
296+
EXPECT_CALL(m_engineMock, stageWidth()).WillOnce(Return(10));
297+
EXPECT_CALL(m_engineMock, stageHeight()).WillOnce(Return(10));
298+
EXPECT_CALL(target, boundingRect()).WillOnce(Return(Rect(-5, 5, 5, -5)));
299+
vm.reset();
300+
vm.run();
301+
302+
ASSERT_EQ(vm.registerCount(), 1);
303+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
304+
305+
// touching ""
306+
EXPECT_CALL(m_engineMock, findTarget("")).WillOnce(Return(-1));
307+
EXPECT_CALL(m_engineMock, targetAt(-1)).WillOnce(Return(nullptr));
308+
vm.setBytecode(bytecode4);
309+
vm.reset();
310+
vm.run();
311+
312+
ASSERT_EQ(vm.registerCount(), 1);
313+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
314+
315+
// touching Sprite2
316+
EXPECT_CALL(m_engineMock, targetAt(1)).WillOnce(Return(&sprite));
317+
EXPECT_CALL(target, touchingClones).WillOnce(Return(false));
318+
vm.setBytecode(bytecode5);
319+
vm.reset();
320+
vm.run();
321+
322+
ASSERT_EQ(vm.registerCount(), 1);
323+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
324+
325+
// touching (invalid)
326+
EXPECT_CALL(m_engineMock, targetAt(-1)).WillOnce(Return(nullptr));
327+
vm.setBytecode(bytecode6);
328+
vm.reset();
329+
vm.run();
330+
331+
ASSERT_EQ(vm.registerCount(), 1);
332+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
333+
334+
EXPECT_CALL(m_engineMock, targetAt(2)).WillOnce(Return(nullptr));
335+
vm.setBytecode(bytecode7);
336+
vm.reset();
337+
vm.run();
338+
339+
ASSERT_EQ(vm.registerCount(), 1);
340+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
341+
342+
// touching mouse-pointer
343+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(168.087));
344+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(175.908));
345+
EXPECT_CALL(target, touchingPoint(168.087, 175.908)).WillOnce(Return(true));
346+
vm.setBytecode(bytecode8);
347+
vm.reset();
348+
vm.run();
349+
350+
ASSERT_EQ(vm.registerCount(), 1);
351+
ASSERT_TRUE(vm.getInput(0, 1)->toBool());
352+
353+
// touching edge
354+
EXPECT_CALL(m_engineMock, stageWidth()).WillOnce(Return(0));
355+
EXPECT_CALL(m_engineMock, stageHeight()).WillOnce(Return(0));
356+
EXPECT_CALL(target, boundingRect()).WillOnce(Return(Rect(-5, 5, 5, -5)));
357+
vm.setBytecode(bytecode9);
358+
vm.reset();
359+
vm.run();
360+
361+
ASSERT_EQ(vm.registerCount(), 1);
362+
ASSERT_TRUE(vm.getInput(0, 1)->toBool());
363+
364+
EXPECT_CALL(m_engineMock, stageWidth()).WillOnce(Return(10));
365+
EXPECT_CALL(m_engineMock, stageHeight()).WillOnce(Return(10));
366+
EXPECT_CALL(target, boundingRect()).WillOnce(Return(Rect(-5, 5, 5, -5)));
367+
vm.reset();
368+
vm.run();
369+
370+
ASSERT_EQ(vm.registerCount(), 1);
371+
ASSERT_FALSE(vm.getInput(0, 1)->toBool());
372+
}
373+
185374
TEST_F(SensingBlocksTest, DistanceTo)
186375
{
187376
Compiler compiler(&m_engineMock);

0 commit comments

Comments
 (0)