22
33#include < scratchcpp/iengine.h>
44#include < scratchcpp/compiler.h>
5+ #include < scratchcpp/compilerconstant.h>
56#include < scratchcpp/sprite.h>
7+ #include < scratchcpp/input.h>
8+ #include < scratchcpp/value.h>
9+ #include < scratchcpp/executioncontext.h>
10+ #include < scratchcpp/thread.h>
11+ #include < scratchcpp/irandomgenerator.h>
12+ #include < scratchcpp/stringptr.h>
13+ #include < scratchcpp/string_functions.h>
614#include < cmath>
15+ #include < utf8.h>
716
817#include " motionblocks.h"
918
@@ -32,6 +41,7 @@ void MotionBlocks::registerBlocks(IEngine *engine)
3241 engine->addCompileFunction (this , " motion_turnright" , &compileTurnRight);
3342 engine->addCompileFunction (this , " motion_turnleft" , &compileTurnLeft);
3443 engine->addCompileFunction (this , " motion_pointindirection" , &compilePointInDirection);
44+ engine->addCompileFunction (this , " motion_pointtowards" , &compilePointTowards);
3545}
3646
3747CompilerValue *MotionBlocks::compileMoveSteps (Compiler *compiler)
@@ -74,6 +84,35 @@ CompilerValue *MotionBlocks::compilePointInDirection(Compiler *compiler)
7484 return nullptr ;
7585}
7686
87+ CompilerValue *MotionBlocks::compilePointTowards (Compiler *compiler)
88+ {
89+ if (compiler->target ()->isStage ())
90+ return nullptr ;
91+
92+ Input *input = compiler->input (" TOWARDS" );
93+
94+ if (input->pointsToDropdownMenu ()) {
95+ std::string value = input->selectedMenuItem ();
96+
97+ if (value == " _mouse_" )
98+ compiler->addTargetFunctionCall (" motion_point_towards_mouse" );
99+ else if (value == " _random_" )
100+ compiler->addFunctionCallWithCtx (" motion_point_towards_random_pos" );
101+ else {
102+ int index = compiler->engine ()->findTarget (value);
103+ Target *anotherTarget = compiler->engine ()->targetAt (index);
104+
105+ if (anotherTarget && !anotherTarget->isStage ())
106+ compiler->addTargetFunctionCall (" motion_point_towards_target_by_index" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { compiler->addConstValue (index) });
107+ }
108+ } else {
109+ CompilerValue *towards = compiler->addInput (input);
110+ compiler->addFunctionCallWithCtx (" motion_pointtowards" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { towards });
111+ }
112+
113+ return nullptr ;
114+ }
115+
77116extern " C" void motion_movesteps (Sprite *sprite, double steps)
78117{
79118 double dir = sprite->direction ();
@@ -94,3 +133,66 @@ extern "C" void motion_pointindirection(Sprite *sprite, double direction)
94133{
95134 sprite->setDirection (direction);
96135}
136+
137+ inline void motion_point_towards_pos (Sprite *sprite, double x, double y)
138+ {
139+ // https://en.scratch-wiki.info/wiki/Point_Towards_()_(block)#Workaround
140+ double deltaX = x - sprite->x ();
141+ double deltaY = y - sprite->y ();
142+
143+ if (deltaY == 0 ) {
144+ if (deltaX < 0 )
145+ sprite->setDirection (-90 );
146+ else
147+ sprite->setDirection (90 );
148+ } else if (deltaY < 0 )
149+ sprite->setDirection (180 + (180 / pi) * std::atan (deltaX / deltaY));
150+ else
151+ sprite->setDirection ((180 / pi) * std::atan (deltaX / deltaY));
152+ }
153+
154+ extern " C" void motion_point_towards_mouse (Sprite *sprite)
155+ {
156+ IEngine *engine = sprite->engine ();
157+ motion_point_towards_pos (sprite, engine->mouseX (), engine->mouseY ());
158+ }
159+
160+ extern " C" void motion_point_towards_random_pos (ExecutionContext *ctx)
161+ {
162+ Sprite *sprite = static_cast <Sprite *>(ctx->thread ()->target ());
163+ IEngine *engine = ctx->engine ();
164+ const int stageWidth = engine->stageWidth ();
165+ const int stageHeight = engine->stageHeight ();
166+ IRandomGenerator *rng = ctx->rng ();
167+ motion_point_towards_pos (sprite, rng->randintDouble (-stageWidth / 2.0 , stageWidth / 2.0 ), rng->randintDouble (-stageHeight / 2.0 , stageHeight / 2.0 ));
168+ }
169+
170+ extern " C" void motion_point_towards_target_by_index (Sprite *sprite, double index)
171+ {
172+ Sprite *anotherSprite = static_cast <Sprite *>(sprite->engine ()->targetAt (index));
173+ motion_point_towards_pos (sprite, anotherSprite->x (), anotherSprite->y ());
174+ }
175+
176+ extern " C" void motion_pointtowards (ExecutionContext *ctx, const StringPtr *towards)
177+ {
178+ static const StringPtr MOUSE_STR (" _mouse_" );
179+ static const StringPtr RANDOM_STR (" _random_" );
180+
181+ Sprite *sprite = static_cast <Sprite *>(ctx->thread ()->target ());
182+
183+ if (string_compare_case_sensitive (towards, &MOUSE_STR) == 0 )
184+ motion_point_towards_mouse (sprite);
185+ else if (string_compare_case_sensitive (towards, &RANDOM_STR) == 0 )
186+ motion_point_towards_random_pos (ctx);
187+ else {
188+ // TODO: Use UTF-16 in engine
189+ std::string u8name = utf8::utf16to8 (std::u16string (towards->data ));
190+ IEngine *engine = ctx->engine ();
191+ Target *anotherTarget = engine->targetAt (engine->findTarget (u8name));
192+
193+ if (anotherTarget && !anotherTarget->isStage ()) {
194+ Sprite *anotherSprite = static_cast <Sprite *>(anotherTarget);
195+ motion_point_towards_pos (sprite, anotherSprite->x (), anotherSprite->y ());
196+ }
197+ }
198+ }
0 commit comments