33#include < scratchcpp/iengine.h>
44#include < scratchcpp/compiler.h>
55#include < scratchcpp/sprite.h>
6+ #include < scratchcpp/input.h>
67
78#include " motionblocks.h"
9+ #include " ../engine/internal/randomgenerator.h"
810
911using namespace libscratchcpp ;
1012
1113static const double pi = std::acos(-1 ); // TODO: Use std::numbers::pi in C++20
1214
15+ IRandomGenerator *MotionBlocks::rng = nullptr ;
16+
1317std::string MotionBlocks::name () const
1418{
1519 return " Motion" ;
@@ -22,11 +26,13 @@ void MotionBlocks::registerBlocks(IEngine *engine)
2226 engine->addCompileFunction (this , " motion_turnright" , &compileTurnRight);
2327 engine->addCompileFunction (this , " motion_turnleft" , &compileTurnLeft);
2428 engine->addCompileFunction (this , " motion_pointindirection" , &compilePointInDirection);
29+ engine->addCompileFunction (this , " motion_pointtowards" , &compilePointTowards);
2530
2631 // Inputs
2732 engine->addInput (this , " STEPS" , STEPS);
2833 engine->addInput (this , " DEGREES" , DEGREES);
2934 engine->addInput (this , " DIRECTION" , DIRECTION);
35+ engine->addInput (this , " TOWARDS" , TOWARDS);
3036}
3137
3238void MotionBlocks::compileMoveSteps (Compiler *compiler)
@@ -53,6 +59,29 @@ void MotionBlocks::compilePointInDirection(Compiler *compiler)
5359 compiler->addFunctionCall (&pointInDirection);
5460}
5561
62+ void MotionBlocks::compilePointTowards (Compiler *compiler)
63+ {
64+ Input *input = compiler->input (TOWARDS);
65+
66+ if (input->type () != Input::Type::ObscuredShadow) {
67+ assert (input->pointsToDropdownMenu ());
68+ std::string value = input->selectedMenuItem ();
69+
70+ if (value == " _mouse_" )
71+ compiler->addFunctionCall (&pointTowardsMousePointer);
72+ else if (value == " _random_" )
73+ compiler->addFunctionCall (&pointTowardsRandomPosition);
74+ else {
75+ int index = compiler->engine ()->findTarget (value);
76+ compiler->addConstValue (index);
77+ compiler->addFunctionCall (&pointTowardsByIndex);
78+ }
79+ } else {
80+ compiler->addInput (input);
81+ compiler->addFunctionCall (&pointTowards);
82+ }
83+ }
84+
5685unsigned int MotionBlocks::moveSteps (VirtualMachine *vm)
5786{
5887 Sprite *sprite = dynamic_cast <Sprite *>(vm->target ());
@@ -96,3 +125,81 @@ unsigned int MotionBlocks::pointInDirection(VirtualMachine *vm)
96125
97126 return 1 ;
98127}
128+
129+ void MotionBlocks::pointTowardsPos (Sprite *sprite, double x, double y)
130+ {
131+ if (!sprite)
132+ return ;
133+
134+ // https://en.scratch-wiki.info/wiki/Point_Towards_()_(block)#Workaround
135+ double deltaX = x - sprite->x ();
136+ double deltaY = y - sprite->y ();
137+
138+ if (deltaY == 0 ) {
139+ if (deltaX < 0 )
140+ sprite->setDirection (-90 );
141+ else
142+ sprite->setDirection (90 );
143+ } else if (deltaY < 0 )
144+ sprite->setDirection (180 + (180 / pi) * std::atan (deltaX / deltaY));
145+ else
146+ sprite->setDirection ((180 / pi) * std::atan (deltaX / deltaY));
147+ }
148+
149+ unsigned int MotionBlocks::pointTowards (VirtualMachine *vm)
150+ {
151+ std::string value = vm->getInput (0 , 1 )->toString ();
152+ Target *target;
153+
154+ if (value == " _mouse_" )
155+ pointTowardsPos (dynamic_cast <Sprite *>(vm->target ()), vm->engine ()->mouseX (), vm->engine ()->mouseY ());
156+ else if (value == " _random_" ) {
157+ // TODO: Read stage size from engine (#224)
158+ static const unsigned int stageWidth = 480 ;
159+ static const unsigned int stageHeight = 360 ;
160+
161+ if (!rng)
162+ rng = RandomGenerator::instance ().get ();
163+
164+ pointTowardsPos (dynamic_cast <Sprite *>(vm->target ()), rng->randint (-static_cast <int >(stageWidth / 2 ), stageWidth / 2 ), rng->randint (-static_cast <int >(stageHeight / 2 ), stageHeight / 2 ));
165+ } else {
166+ target = vm->engine ()->targetAt (vm->engine ()->findTarget (value));
167+ Sprite *sprite = dynamic_cast <Sprite *>(target);
168+
169+ if (sprite)
170+ pointTowardsPos (dynamic_cast <Sprite *>(vm->target ()), sprite->x (), sprite->y ());
171+ }
172+
173+ return 1 ;
174+ }
175+
176+ unsigned int MotionBlocks::pointTowardsByIndex (VirtualMachine *vm)
177+ {
178+ Target *target = vm->engine ()->targetAt (vm->getInput (0 , 1 )->toInt ());
179+ Sprite *sprite = dynamic_cast <Sprite *>(target);
180+
181+ if (sprite)
182+ pointTowardsPos (dynamic_cast <Sprite *>(vm->target ()), sprite->x (), sprite->y ());
183+
184+ return 1 ;
185+ }
186+
187+ unsigned int MotionBlocks::pointTowardsMousePointer (VirtualMachine *vm)
188+ {
189+ pointTowardsPos (dynamic_cast <Sprite *>(vm->target ()), vm->engine ()->mouseX (), vm->engine ()->mouseY ());
190+ return 0 ;
191+ }
192+
193+ unsigned int MotionBlocks::pointTowardsRandomPosition (VirtualMachine *vm)
194+ {
195+ // TODO: Read stage size from engine (#224)
196+ static const unsigned int stageWidth = 480 ;
197+ static const unsigned int stageHeight = 360 ;
198+
199+ if (!rng)
200+ rng = RandomGenerator::instance ().get ();
201+
202+ pointTowardsPos (dynamic_cast <Sprite *>(vm->target ()), rng->randint (-static_cast <int >(stageWidth / 2 ), stageWidth / 2 ), rng->randint (-static_cast <int >(stageHeight / 2 ), stageHeight / 2 ));
203+
204+ return 0 ;
205+ }
0 commit comments