Skip to content

Commit d0b16f3

Browse files
committed
Implement motion_goto block
1 parent a26cf40 commit d0b16f3

File tree

3 files changed

+255
-1
lines changed

3 files changed

+255
-1
lines changed

src/blocks/motionblocks.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ void MotionBlocks::registerBlocks(IEngine *engine)
2828
engine->addCompileFunction(this, "motion_pointindirection", &compilePointInDirection);
2929
engine->addCompileFunction(this, "motion_pointtowards", &compilePointTowards);
3030
engine->addCompileFunction(this, "motion_gotoxy", &compileGoToXY);
31+
engine->addCompileFunction(this, "motion_goto", &compileGoTo);
3132

3233
// Inputs
3334
engine->addInput(this, "STEPS", STEPS);
@@ -36,6 +37,7 @@ void MotionBlocks::registerBlocks(IEngine *engine)
3637
engine->addInput(this, "TOWARDS", TOWARDS);
3738
engine->addInput(this, "X", X);
3839
engine->addInput(this, "Y", Y);
40+
engine->addInput(this, "TO", TO);
3941
}
4042

4143
void MotionBlocks::compileMoveSteps(Compiler *compiler)
@@ -92,6 +94,29 @@ void MotionBlocks::compileGoToXY(Compiler *compiler)
9294
compiler->addFunctionCall(&goToXY);
9395
}
9496

97+
void MotionBlocks::compileGoTo(Compiler *compiler)
98+
{
99+
Input *input = compiler->input(TO);
100+
101+
if (input->type() != Input::Type::ObscuredShadow) {
102+
assert(input->pointsToDropdownMenu());
103+
std::string value = input->selectedMenuItem();
104+
105+
if (value == "_mouse_")
106+
compiler->addFunctionCall(&goToMousePointer);
107+
else if (value == "_random_")
108+
compiler->addFunctionCall(&goToRandomPosition);
109+
else {
110+
int index = compiler->engine()->findTarget(value);
111+
compiler->addConstValue(index);
112+
compiler->addFunctionCall(&goToByIndex);
113+
}
114+
} else {
115+
compiler->addInput(input);
116+
compiler->addFunctionCall(&goTo);
117+
}
118+
}
119+
95120
unsigned int MotionBlocks::moveSteps(VirtualMachine *vm)
96121
{
97122
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
@@ -225,3 +250,82 @@ unsigned int MotionBlocks::goToXY(VirtualMachine *vm)
225250

226251
return 2;
227252
}
253+
254+
unsigned int MotionBlocks::goTo(VirtualMachine *vm)
255+
{
256+
std::string value = vm->getInput(0, 1)->toString();
257+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
258+
259+
if (!sprite)
260+
return 1;
261+
262+
if (value == "_mouse_") {
263+
sprite->setX(vm->engine()->mouseX());
264+
sprite->setY(vm->engine()->mouseY());
265+
} else if (value == "_random_") {
266+
// TODO: Read stage size from engine (#224)
267+
static const unsigned int stageWidth = 480;
268+
static const unsigned int stageHeight = 360;
269+
270+
if (!rng)
271+
rng = RandomGenerator::instance().get();
272+
273+
sprite->setX(rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2));
274+
sprite->setY(rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2));
275+
} else {
276+
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value));
277+
Sprite *targetSprite = dynamic_cast<Sprite *>(target);
278+
279+
if (targetSprite) {
280+
sprite->setX(targetSprite->x());
281+
sprite->setY(targetSprite->y());
282+
}
283+
}
284+
285+
return 1;
286+
}
287+
288+
unsigned int MotionBlocks::goToByIndex(VirtualMachine *vm)
289+
{
290+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
291+
Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt());
292+
Sprite *targetSprite = dynamic_cast<Sprite *>(target);
293+
294+
if (sprite && targetSprite) {
295+
sprite->setX(targetSprite->x());
296+
sprite->setY(targetSprite->y());
297+
}
298+
299+
return 1;
300+
}
301+
302+
unsigned int MotionBlocks::goToMousePointer(VirtualMachine *vm)
303+
{
304+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
305+
306+
if (sprite) {
307+
sprite->setX(vm->engine()->mouseX());
308+
sprite->setY(vm->engine()->mouseY());
309+
}
310+
311+
return 0;
312+
}
313+
314+
unsigned int MotionBlocks::goToRandomPosition(VirtualMachine *vm)
315+
{
316+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
317+
318+
if (sprite) {
319+
// TODO: Read stage size from engine (#224)
320+
static const unsigned int stageWidth = 480;
321+
static const unsigned int stageHeight = 360;
322+
323+
if (!rng)
324+
rng = RandomGenerator::instance().get();
325+
326+
sprite->setX(rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2));
327+
sprite->setY(rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2));
328+
}
329+
330+
return 0;
331+
}

src/blocks/motionblocks.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class MotionBlocks : public IBlockSection
2121
DIRECTION,
2222
TOWARDS,
2323
X,
24-
Y
24+
Y,
25+
TO
2526
};
2627

2728
enum Fields
@@ -42,6 +43,7 @@ class MotionBlocks : public IBlockSection
4243
static void compilePointInDirection(Compiler *compiler);
4344
static void compilePointTowards(Compiler *compiler);
4445
static void compileGoToXY(Compiler *compiler);
46+
static void compileGoTo(Compiler *compiler);
4547

4648
static unsigned int moveSteps(VirtualMachine *vm);
4749
static unsigned int turnRight(VirtualMachine *vm);
@@ -56,6 +58,10 @@ class MotionBlocks : public IBlockSection
5658
static unsigned int pointTowardsRandomPosition(VirtualMachine *vm);
5759

5860
static unsigned int goToXY(VirtualMachine *vm);
61+
static unsigned int goTo(VirtualMachine *vm);
62+
static unsigned int goToByIndex(VirtualMachine *vm);
63+
static unsigned int goToMousePointer(VirtualMachine *vm);
64+
static unsigned int goToRandomPosition(VirtualMachine *vm);
5965

6066
static IRandomGenerator *rng;
6167
};

test/blocks/motion_blocks_test.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ TEST_F(MotionBlocksTest, RegisterBlocks)
100100
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_pointindirection", &MotionBlocks::compilePointInDirection));
101101
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_pointtowards", &MotionBlocks::compilePointTowards));
102102
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_gotoxy", &MotionBlocks::compileGoToXY));
103+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_goto", &MotionBlocks::compileGoTo));
103104

104105
// Inputs
105106
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "STEPS", MotionBlocks::STEPS));
@@ -108,6 +109,7 @@ TEST_F(MotionBlocksTest, RegisterBlocks)
108109
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "TOWARDS", MotionBlocks::TOWARDS));
109110
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "X", MotionBlocks::X));
110111
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "Y", MotionBlocks::Y));
112+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "TO", MotionBlocks::TO));
111113

112114
m_section->registerBlocks(&m_engineMock);
113115
}
@@ -478,3 +480,145 @@ TEST_F(MotionBlocksTest, GoToXYImpl)
478480
ASSERT_EQ(sprite.x(), 95.2);
479481
ASSERT_EQ(sprite.y(), -175.9);
480482
}
483+
484+
TEST_F(MotionBlocksTest, GoTo)
485+
{
486+
Compiler compiler(&m_engineMock);
487+
488+
// go to (mouse-pointer)
489+
auto block1 = std::make_shared<Block>("a", "motion_goto");
490+
addDropdownInput(block1, "TO", MotionBlocks::TO, "_mouse_");
491+
492+
// go to (random position)
493+
auto block2 = std::make_shared<Block>("b", "motion_goto");
494+
addDropdownInput(block2, "TO", MotionBlocks::TO, "_random_");
495+
496+
// go to (Sprite2)
497+
auto block3 = std::make_shared<Block>("c", "motion_goto");
498+
addDropdownInput(block3, "TO", MotionBlocks::TO, "Sprite2");
499+
500+
// go to (join "" "")
501+
auto joinBlock = std::make_shared<Block>("e", "operator_join");
502+
joinBlock->setCompileFunction(&OperatorBlocks::compileJoin);
503+
auto block4 = std::make_shared<Block>("d", "motion_goto");
504+
addDropdownInput(block4, "TO", MotionBlocks::TO, "", joinBlock);
505+
506+
compiler.init();
507+
508+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::goToMousePointer)).WillOnce(Return(0));
509+
compiler.setBlock(block1);
510+
MotionBlocks::compileGoTo(&compiler);
511+
512+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::goToRandomPosition)).WillOnce(Return(1));
513+
compiler.setBlock(block2);
514+
MotionBlocks::compileGoTo(&compiler);
515+
516+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(5));
517+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::goToByIndex)).WillOnce(Return(2));
518+
compiler.setBlock(block3);
519+
MotionBlocks::compileGoTo(&compiler);
520+
521+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::goTo)).WillOnce(Return(3));
522+
compiler.setBlock(block4);
523+
MotionBlocks::compileGoTo(&compiler);
524+
525+
compiler.end();
526+
527+
ASSERT_EQ(
528+
compiler.bytecode(),
529+
std::vector<unsigned int>({ vm::OP_START, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_NULL, vm::OP_STR_CONCAT, vm::OP_EXEC, 3, vm::OP_HALT }));
530+
ASSERT_EQ(compiler.constValues().size(), 1);
531+
ASSERT_EQ(compiler.constValues()[0].toDouble(), 5);
532+
}
533+
534+
TEST_F(MotionBlocksTest, GoToImpl)
535+
{
536+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };
537+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT };
538+
static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT };
539+
static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_EXEC, 1, vm::OP_HALT };
540+
static unsigned int bytecode5[] = { vm::OP_START, vm::OP_EXEC, 2, vm::OP_HALT };
541+
static unsigned int bytecode6[] = { vm::OP_START, vm::OP_EXEC, 3, vm::OP_HALT };
542+
static BlockFunc functions[] = { &MotionBlocks::goTo, &MotionBlocks::goToByIndex, &MotionBlocks::goToMousePointer, &MotionBlocks::goToRandomPosition };
543+
static Value constValues[] = { "_mouse_", "_random_", "Sprite2", 3 };
544+
545+
Sprite sprite;
546+
sprite.setX(70.1);
547+
sprite.setY(-100.025);
548+
549+
Sprite anotherSprite;
550+
anotherSprite.setX(-195.25);
551+
anotherSprite.setY(148.02);
552+
553+
VirtualMachine vm(&sprite, &m_engineMock, nullptr);
554+
vm.setFunctions(functions);
555+
vm.setConstValues(constValues);
556+
557+
RandomGeneratorMock rng;
558+
MotionBlocks::rng = &rng;
559+
560+
// go to (join "_mouse_" "")
561+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(70.56));
562+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(45.2));
563+
564+
vm.setBytecode(bytecode1);
565+
vm.run();
566+
567+
ASSERT_EQ(vm.registerCount(), 0);
568+
ASSERT_EQ(sprite.x(), 70.56);
569+
ASSERT_EQ(sprite.y(), 45.2);
570+
571+
// go to (join "_random_" "")
572+
EXPECT_CALL(rng, randint(-240, 240)).WillOnce(Return(-158));
573+
EXPECT_CALL(rng, randint(-180, 180)).WillOnce(Return(65));
574+
575+
vm.setBytecode(bytecode2);
576+
vm.run();
577+
578+
ASSERT_EQ(vm.registerCount(), 0);
579+
ASSERT_EQ(sprite.x(), -158);
580+
ASSERT_EQ(sprite.y(), 65);
581+
582+
// go to (join "Sprite2" "")
583+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(3));
584+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&anotherSprite));
585+
586+
vm.setBytecode(bytecode3);
587+
vm.run();
588+
589+
ASSERT_EQ(vm.registerCount(), 0);
590+
ASSERT_EQ(sprite.x(), anotherSprite.x());
591+
ASSERT_EQ(sprite.y(), anotherSprite.y());
592+
593+
// go to (Sprite2)
594+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&anotherSprite));
595+
596+
vm.setBytecode(bytecode4);
597+
vm.run();
598+
599+
ASSERT_EQ(vm.registerCount(), 0);
600+
ASSERT_EQ(sprite.x(), anotherSprite.x());
601+
ASSERT_EQ(sprite.y(), anotherSprite.y());
602+
603+
// go to (mouse-pointer)
604+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(-239.92));
605+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(-170.6));
606+
607+
vm.setBytecode(bytecode5);
608+
vm.run();
609+
610+
ASSERT_EQ(vm.registerCount(), 0);
611+
ASSERT_EQ(sprite.x(), -239.92);
612+
ASSERT_EQ(sprite.y(), -170.6);
613+
614+
// go to (random position)
615+
EXPECT_CALL(rng, randint(-240, 240)).WillOnce(Return(220));
616+
EXPECT_CALL(rng, randint(-180, 180)).WillOnce(Return(-16));
617+
618+
vm.setBytecode(bytecode6);
619+
vm.run();
620+
621+
ASSERT_EQ(vm.registerCount(), 0);
622+
ASSERT_EQ(sprite.x(), 220);
623+
ASSERT_EQ(sprite.y(), -16);
624+
}

0 commit comments

Comments
 (0)