Skip to content

Commit e789021

Browse files
committed
Implement motion_glideto block
1 parent 6b8c1cf commit e789021

File tree

3 files changed

+312
-3
lines changed

3 files changed

+312
-3
lines changed

src/blocks/motionblocks.cpp

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ void MotionBlocks::registerBlocks(IEngine *engine)
3232
engine->addCompileFunction(this, "motion_gotoxy", &compileGoToXY);
3333
engine->addCompileFunction(this, "motion_goto", &compileGoTo);
3434
engine->addCompileFunction(this, "motion_glidesecstoxy", &compileGlideSecsToXY);
35+
engine->addCompileFunction(this, "motion_glideto", &compileGlideTo);
3536

3637
// Inputs
3738
engine->addInput(this, "STEPS", STEPS);
@@ -130,6 +131,33 @@ void MotionBlocks::compileGlideSecsToXY(Compiler *compiler)
130131
compiler->addFunctionCall(&glideSecsTo);
131132
}
132133

134+
void MotionBlocks::compileGlideTo(Compiler *compiler)
135+
{
136+
compiler->addInput(SECS);
137+
138+
Input *input = compiler->input(TO);
139+
140+
if (input->type() != Input::Type::ObscuredShadow) {
141+
assert(input->pointsToDropdownMenu());
142+
std::string value = input->selectedMenuItem();
143+
144+
if (value == "_mouse_")
145+
compiler->addFunctionCall(&startGlideToMousePointer);
146+
else if (value == "_random_")
147+
compiler->addFunctionCall(&startGlideToRandomPosition);
148+
else {
149+
int index = compiler->engine()->findTarget(value);
150+
compiler->addConstValue(index);
151+
compiler->addFunctionCall(&startGlideToByIndex);
152+
}
153+
} else {
154+
compiler->addInput(input);
155+
compiler->addFunctionCall(&startGlideTo);
156+
}
157+
158+
compiler->addFunctionCall(&glideSecsTo);
159+
}
160+
133161
unsigned int MotionBlocks::moveSteps(VirtualMachine *vm)
134162
{
135163
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
@@ -265,12 +293,13 @@ unsigned int MotionBlocks::goToXY(VirtualMachine *vm)
265293

266294
unsigned int MotionBlocks::goTo(VirtualMachine *vm)
267295
{
268-
std::string value = vm->getInput(0, 1)->toString();
269296
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
270297

271298
if (!sprite)
272299
return 1;
273300

301+
std::string value = vm->getInput(0, 1)->toString();
302+
274303
if (value == "_mouse_") {
275304
sprite->setX(vm->engine()->mouseX());
276305
sprite->setY(vm->engine()->mouseY());
@@ -419,3 +448,74 @@ unsigned int MotionBlocks::glideSecsTo(VirtualMachine *vm)
419448

420449
return 0;
421450
}
451+
452+
unsigned int MotionBlocks::startGlideTo(VirtualMachine *vm)
453+
{
454+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
455+
456+
if (!sprite)
457+
return 1;
458+
459+
std::string value = vm->getInput(1, 2)->toString();
460+
461+
if (value == "_mouse_")
462+
startGlidingToPos(vm, vm->engine()->mouseX(), vm->engine()->mouseY(), vm->getInput(0, 2)->toDouble());
463+
else if (value == "_random_") {
464+
// TODO: Read stage size from engine (#224)
465+
static const unsigned int stageWidth = 480;
466+
static const unsigned int stageHeight = 360;
467+
468+
if (!rng)
469+
rng = RandomGenerator::instance().get();
470+
471+
startGlidingToPos(vm, rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2), vm->getInput(0, 2)->toDouble());
472+
} else {
473+
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value));
474+
Sprite *targetSprite = dynamic_cast<Sprite *>(target);
475+
476+
if (targetSprite)
477+
startGlidingToPos(vm, targetSprite->x(), targetSprite->y(), vm->getInput(0, 2)->toDouble());
478+
}
479+
480+
return 2;
481+
}
482+
483+
unsigned int MotionBlocks::startGlideToByIndex(VirtualMachine *vm)
484+
{
485+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
486+
Target *target = vm->engine()->targetAt(vm->getInput(1, 2)->toInt());
487+
Sprite *targetSprite = dynamic_cast<Sprite *>(target);
488+
489+
if (sprite && targetSprite)
490+
startGlidingToPos(vm, targetSprite->x(), targetSprite->y(), vm->getInput(0, 2)->toDouble());
491+
492+
return 2;
493+
}
494+
495+
unsigned int MotionBlocks::startGlideToMousePointer(VirtualMachine *vm)
496+
{
497+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
498+
499+
if (sprite)
500+
startGlidingToPos(vm, vm->engine()->mouseX(), vm->engine()->mouseY(), vm->getInput(0, 1)->toDouble());
501+
502+
return 1;
503+
}
504+
505+
unsigned int MotionBlocks::startGlideToRandomPosition(VirtualMachine *vm)
506+
{
507+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
508+
509+
if (sprite) {
510+
// TODO: Read stage size from engine (#224)
511+
static const unsigned int stageWidth = 480;
512+
static const unsigned int stageHeight = 360;
513+
514+
if (!rng)
515+
rng = RandomGenerator::instance().get();
516+
517+
startGlidingToPos(vm, rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2), vm->getInput(0, 1)->toDouble());
518+
}
519+
520+
return 1;
521+
}

src/blocks/motionblocks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class MotionBlocks : public IBlockSection
5050
static void compileGoToXY(Compiler *compiler);
5151
static void compileGoTo(Compiler *compiler);
5252
static void compileGlideSecsToXY(Compiler *compiler);
53+
static void compileGlideTo(Compiler *compiler);
5354

5455
static unsigned int moveSteps(VirtualMachine *vm);
5556
static unsigned int turnRight(VirtualMachine *vm);
@@ -74,6 +75,10 @@ class MotionBlocks : public IBlockSection
7475

7576
static unsigned int startGlideSecsTo(VirtualMachine *vm);
7677
static unsigned int glideSecsTo(VirtualMachine *vm);
78+
static unsigned int startGlideTo(VirtualMachine *vm);
79+
static unsigned int startGlideToByIndex(VirtualMachine *vm);
80+
static unsigned int startGlideToMousePointer(VirtualMachine *vm);
81+
static unsigned int startGlideToRandomPosition(VirtualMachine *vm);
7782

7883
static IRandomGenerator *rng;
7984
static IClock *clock;

test/blocks/motion_blocks_test.cpp

Lines changed: 206 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ TEST_F(MotionBlocksTest, RegisterBlocks)
105105
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_gotoxy", &MotionBlocks::compileGoToXY));
106106
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_goto", &MotionBlocks::compileGoTo));
107107
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_glidesecstoxy", &MotionBlocks::compileGlideSecsToXY));
108+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "motion_glideto", &MotionBlocks::compileGlideTo));
108109

109110
// Inputs
110111
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "STEPS", MotionBlocks::STEPS));
@@ -660,8 +661,8 @@ TEST_F(MotionBlocksTest, GlideSecsToXYImpl)
660661
static BlockFunc functions[] = { &MotionBlocks::startGlideSecsTo, &MotionBlocks::glideSecsTo };
661662
static Value constValues[] = { 2.5, 95.2, -175.9 };
662663

663-
double startX = 100.32;
664-
double startY = -50.12;
664+
static const double startX = 100.32;
665+
static const double startY = -50.12;
665666

666667
Sprite sprite;
667668
sprite.setX(startX);
@@ -716,3 +717,206 @@ TEST_F(MotionBlocksTest, GlideSecsToXYImpl)
716717

717718
MotionBlocks::clock = Clock::instance().get();
718719
}
720+
721+
TEST_F(MotionBlocksTest, GlideTo)
722+
{
723+
Compiler compiler(&m_engineMock);
724+
725+
// glide (3.25) secs to (mouse-pointer)
726+
auto block1 = std::make_shared<Block>("a", "motion_glideto");
727+
addValueInput(block1, "SECS", MotionBlocks::SECS, 3.25);
728+
addDropdownInput(block1, "TO", MotionBlocks::TO, "_mouse_");
729+
730+
// glide (2.5) secs to (random position)
731+
auto block2 = std::make_shared<Block>("b", "motion_glideto");
732+
addValueInput(block2, "SECS", MotionBlocks::SECS, 2.5);
733+
addDropdownInput(block2, "TO", MotionBlocks::TO, "_random_");
734+
735+
// glide (5.01) secs to (Sprite2)
736+
auto block3 = std::make_shared<Block>("c", "motion_glideto");
737+
addValueInput(block3, "SECS", MotionBlocks::SECS, 3.25);
738+
addDropdownInput(block3, "TO", MotionBlocks::TO, "Sprite2");
739+
740+
// glide (6.5) secs to (join "" "")
741+
auto joinBlock = std::make_shared<Block>("e", "operator_join");
742+
joinBlock->setCompileFunction(&OperatorBlocks::compileJoin);
743+
auto block4 = std::make_shared<Block>("d", "motion_glideto");
744+
addValueInput(block4, "SECS", MotionBlocks::SECS, 6.5);
745+
addDropdownInput(block4, "TO", MotionBlocks::TO, "", joinBlock);
746+
747+
compiler.init();
748+
749+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::startGlideToMousePointer)).WillOnce(Return(0));
750+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::glideSecsTo)).WillOnce(Return(4));
751+
compiler.setBlock(block1);
752+
MotionBlocks::compileGlideTo(&compiler);
753+
754+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::startGlideToRandomPosition)).WillOnce(Return(1));
755+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::glideSecsTo)).WillOnce(Return(4));
756+
compiler.setBlock(block2);
757+
MotionBlocks::compileGlideTo(&compiler);
758+
759+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(5));
760+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::startGlideToByIndex)).WillOnce(Return(2));
761+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::glideSecsTo)).WillOnce(Return(4));
762+
compiler.setBlock(block3);
763+
MotionBlocks::compileGlideTo(&compiler);
764+
765+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::startGlideTo)).WillOnce(Return(3));
766+
EXPECT_CALL(m_engineMock, functionIndex(&MotionBlocks::glideSecsTo)).WillOnce(Return(4));
767+
compiler.setBlock(block4);
768+
MotionBlocks::compileGlideTo(&compiler);
769+
770+
compiler.end();
771+
772+
ASSERT_EQ(
773+
compiler.bytecode(),
774+
std::vector<unsigned int>(
775+
{ vm::OP_START,
776+
vm::OP_CONST,
777+
0,
778+
vm::OP_EXEC,
779+
0,
780+
vm::OP_EXEC,
781+
4,
782+
vm::OP_CONST,
783+
1,
784+
vm::OP_EXEC,
785+
1,
786+
vm::OP_EXEC,
787+
4,
788+
vm::OP_CONST,
789+
2,
790+
vm::OP_CONST,
791+
3,
792+
vm::OP_EXEC,
793+
2,
794+
vm::OP_EXEC,
795+
4,
796+
vm::OP_CONST,
797+
4,
798+
vm::OP_NULL,
799+
vm::OP_NULL,
800+
vm::OP_STR_CONCAT,
801+
vm::OP_EXEC,
802+
3,
803+
vm::OP_EXEC,
804+
4,
805+
vm::OP_HALT }));
806+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 3.25, 2.5, 3.25, 5, 6.5 }));
807+
}
808+
809+
TEST_F(MotionBlocksTest, GlideToImpl)
810+
{
811+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 4, vm::OP_HALT };
812+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 2, vm::OP_EXEC, 1, vm::OP_EXEC, 4, vm::OP_HALT };
813+
static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_EXEC, 4, vm::OP_HALT };
814+
static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 3, vm::OP_EXEC, 4, vm::OP_HALT };
815+
static unsigned int *scripts[] = { bytecode1, bytecode2, bytecode3, bytecode4 };
816+
static BlockFunc functions
817+
[] = { &MotionBlocks::startGlideTo, &MotionBlocks::startGlideToByIndex, &MotionBlocks::startGlideToMousePointer, &MotionBlocks::startGlideToRandomPosition, &MotionBlocks::glideSecsTo };
818+
static Value constValues[] = { 2.5, "Sprite2", 3 };
819+
820+
static const double startX = 100.32;
821+
static const double startY = -50.12;
822+
static const double endX = 95.2;
823+
static const double endY = -175.9;
824+
825+
Sprite sprite;
826+
Sprite anotherSprite;
827+
anotherSprite.setX(endX);
828+
anotherSprite.setY(endY);
829+
830+
VirtualMachine vm(&sprite, &m_engineMock, nullptr);
831+
vm.setFunctions(functions);
832+
vm.setConstValues(constValues);
833+
834+
ClockMock clock;
835+
RandomGeneratorMock rng;
836+
MotionBlocks::clock = &clock;
837+
MotionBlocks::rng = &rng;
838+
int i = 0;
839+
840+
for (auto script : scripts) {
841+
sprite.setX(startX);
842+
sprite.setY(startY);
843+
844+
switch (i) {
845+
case 0:
846+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(3));
847+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&anotherSprite));
848+
break;
849+
850+
case 1:
851+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&anotherSprite));
852+
break;
853+
854+
case 2:
855+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(endX));
856+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(endY));
857+
break;
858+
case 3:
859+
EXPECT_CALL(rng, randint(-240, 240)).WillOnce(Return(std::round(endX)));
860+
EXPECT_CALL(rng, randint(-180, 180)).WillOnce(Return(std::round(endY)));
861+
default:
862+
break;
863+
}
864+
865+
vm.setBytecode(script);
866+
std::chrono::steady_clock::time_point startTime(std::chrono::milliseconds(1000));
867+
EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime));
868+
EXPECT_CALL(m_engineMock, lockFrame()).Times(1);
869+
EXPECT_CALL(m_engineMock, breakFrame()).Times(1);
870+
vm.run();
871+
872+
ASSERT_EQ(vm.registerCount(), 0);
873+
ASSERT_TRUE(MotionBlocks::m_timeMap.find(&vm) != MotionBlocks::m_timeMap.cend());
874+
ASSERT_TRUE(MotionBlocks::m_glideMap.find(&vm) != MotionBlocks::m_glideMap.cend());
875+
ASSERT_FALSE(vm.atEnd());
876+
ASSERT_EQ(sprite.x(), startX);
877+
ASSERT_EQ(sprite.y(), startY);
878+
879+
std::chrono::steady_clock::time_point time1(std::chrono::milliseconds(3456));
880+
EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time1));
881+
EXPECT_CALL(m_engineMock, lockFrame()).Times(1);
882+
EXPECT_CALL(m_engineMock, breakFrame()).Times(1);
883+
vm.run();
884+
885+
ASSERT_EQ(vm.registerCount(), 0);
886+
ASSERT_TRUE(MotionBlocks::m_timeMap.find(&vm) != MotionBlocks::m_timeMap.cend());
887+
ASSERT_TRUE(MotionBlocks::m_glideMap.find(&vm) != MotionBlocks::m_glideMap.cend());
888+
ASSERT_FALSE(vm.atEnd());
889+
890+
if (i == 3) {
891+
ASSERT_EQ(std::round(sprite.x() * 100) / 100, 95.09);
892+
ASSERT_EQ(std::round(sprite.y() * 100) / 100, -173.78);
893+
} else {
894+
ASSERT_EQ(std::round(sprite.x() * 100) / 100, 95.29);
895+
ASSERT_EQ(std::round(sprite.y() * 100) / 100, -173.69);
896+
}
897+
898+
std::chrono::steady_clock::time_point time2(std::chrono::milliseconds(3500));
899+
EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2));
900+
EXPECT_CALL(m_engineMock, lockFrame()).Times(0);
901+
EXPECT_CALL(m_engineMock, breakFrame()).Times(0);
902+
vm.run();
903+
904+
ASSERT_EQ(vm.registerCount(), 0);
905+
ASSERT_TRUE(MotionBlocks::m_timeMap.find(&vm) == MotionBlocks::m_timeMap.cend());
906+
ASSERT_TRUE(MotionBlocks::m_glideMap.find(&vm) == MotionBlocks::m_glideMap.cend());
907+
ASSERT_TRUE(vm.atEnd());
908+
909+
if (i == 3) {
910+
ASSERT_EQ(sprite.x(), std::round(endX));
911+
ASSERT_EQ(sprite.y(), std::round(endY));
912+
} else {
913+
ASSERT_EQ(std::round(sprite.x() * 100) / 100, endX);
914+
ASSERT_EQ(std::round(sprite.y() * 100) / 100, endY);
915+
}
916+
917+
i++;
918+
}
919+
920+
MotionBlocks::clock = Clock::instance().get();
921+
MotionBlocks::rng = RandomGenerator::instance().get();
922+
}

0 commit comments

Comments
 (0)