|
1 | 1 | // SPDX-License-Identifier: Apache-2.0
|
2 | 2 |
|
| 3 | +#include <scratchcpp/iengine.h> |
| 4 | +#include <scratchcpp/compiler.h> |
| 5 | +#include <scratchcpp/sprite.h> |
| 6 | +#include <scratchcpp/input.h> |
| 7 | + |
3 | 8 | #include "motionblocks.h"
|
| 9 | +#include "../engine/internal/randomgenerator.h" |
4 | 10 |
|
5 | 11 | using namespace libscratchcpp;
|
6 | 12 |
|
| 13 | +static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20 |
| 14 | + |
| 15 | +IRandomGenerator *MotionBlocks::rng = nullptr; |
| 16 | + |
7 | 17 | std::string MotionBlocks::name() const
|
8 | 18 | {
|
9 | 19 | return "Motion";
|
10 | 20 | }
|
11 | 21 |
|
12 | 22 | void MotionBlocks::registerBlocks(IEngine *engine)
|
13 | 23 | {
|
| 24 | + // Blocks |
| 25 | + engine->addCompileFunction(this, "motion_movesteps", &compileMoveSteps); |
| 26 | + engine->addCompileFunction(this, "motion_turnright", &compileTurnRight); |
| 27 | + engine->addCompileFunction(this, "motion_turnleft", &compileTurnLeft); |
| 28 | + engine->addCompileFunction(this, "motion_pointindirection", &compilePointInDirection); |
| 29 | + engine->addCompileFunction(this, "motion_pointtowards", &compilePointTowards); |
| 30 | + engine->addCompileFunction(this, "motion_gotoxy", &compileGoToXY); |
| 31 | + |
| 32 | + // Inputs |
| 33 | + engine->addInput(this, "STEPS", STEPS); |
| 34 | + engine->addInput(this, "DEGREES", DEGREES); |
| 35 | + engine->addInput(this, "DIRECTION", DIRECTION); |
| 36 | + engine->addInput(this, "TOWARDS", TOWARDS); |
| 37 | + engine->addInput(this, "X", X); |
| 38 | + engine->addInput(this, "Y", Y); |
| 39 | +} |
| 40 | + |
| 41 | +void MotionBlocks::compileMoveSteps(Compiler *compiler) |
| 42 | +{ |
| 43 | + compiler->addInput(STEPS); |
| 44 | + compiler->addFunctionCall(&moveSteps); |
| 45 | +} |
| 46 | + |
| 47 | +void MotionBlocks::compileTurnRight(Compiler *compiler) |
| 48 | +{ |
| 49 | + compiler->addInput(DEGREES); |
| 50 | + compiler->addFunctionCall(&turnRight); |
| 51 | +} |
| 52 | + |
| 53 | +void MotionBlocks::compileTurnLeft(Compiler *compiler) |
| 54 | +{ |
| 55 | + compiler->addInput(DEGREES); |
| 56 | + compiler->addFunctionCall(&turnLeft); |
| 57 | +} |
| 58 | + |
| 59 | +void MotionBlocks::compilePointInDirection(Compiler *compiler) |
| 60 | +{ |
| 61 | + compiler->addInput(DIRECTION); |
| 62 | + compiler->addFunctionCall(&pointInDirection); |
| 63 | +} |
| 64 | + |
| 65 | +void MotionBlocks::compilePointTowards(Compiler *compiler) |
| 66 | +{ |
| 67 | + Input *input = compiler->input(TOWARDS); |
| 68 | + |
| 69 | + if (input->type() != Input::Type::ObscuredShadow) { |
| 70 | + assert(input->pointsToDropdownMenu()); |
| 71 | + std::string value = input->selectedMenuItem(); |
| 72 | + |
| 73 | + if (value == "_mouse_") |
| 74 | + compiler->addFunctionCall(&pointTowardsMousePointer); |
| 75 | + else if (value == "_random_") |
| 76 | + compiler->addFunctionCall(&pointTowardsRandomPosition); |
| 77 | + else { |
| 78 | + int index = compiler->engine()->findTarget(value); |
| 79 | + compiler->addConstValue(index); |
| 80 | + compiler->addFunctionCall(&pointTowardsByIndex); |
| 81 | + } |
| 82 | + } else { |
| 83 | + compiler->addInput(input); |
| 84 | + compiler->addFunctionCall(&pointTowards); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +void MotionBlocks::compileGoToXY(Compiler *compiler) |
| 89 | +{ |
| 90 | + compiler->addInput(X); |
| 91 | + compiler->addInput(Y); |
| 92 | + compiler->addFunctionCall(&goToXY); |
| 93 | +} |
| 94 | + |
| 95 | +unsigned int MotionBlocks::moveSteps(VirtualMachine *vm) |
| 96 | +{ |
| 97 | + Sprite *sprite = dynamic_cast<Sprite *>(vm->target()); |
| 98 | + |
| 99 | + if (sprite) { |
| 100 | + double dir = sprite->direction(); |
| 101 | + double steps = vm->getInput(0, 1)->toDouble(); |
| 102 | + sprite->setX(sprite->x() + std::sin(dir * pi / 180) * steps); |
| 103 | + sprite->setY(sprite->y() + std::cos(dir * pi / 180) * steps); |
| 104 | + } |
| 105 | + |
| 106 | + return 1; |
| 107 | +} |
| 108 | + |
| 109 | +unsigned int MotionBlocks::turnRight(VirtualMachine *vm) |
| 110 | +{ |
| 111 | + Sprite *sprite = dynamic_cast<Sprite *>(vm->target()); |
| 112 | + |
| 113 | + if (sprite) |
| 114 | + sprite->setDirection(sprite->direction() + vm->getInput(0, 1)->toDouble()); |
| 115 | + |
| 116 | + return 1; |
| 117 | +} |
| 118 | + |
| 119 | +unsigned int MotionBlocks::turnLeft(VirtualMachine *vm) |
| 120 | +{ |
| 121 | + Sprite *sprite = dynamic_cast<Sprite *>(vm->target()); |
| 122 | + |
| 123 | + if (sprite) |
| 124 | + sprite->setDirection(sprite->direction() - vm->getInput(0, 1)->toDouble()); |
| 125 | + |
| 126 | + return 1; |
| 127 | +} |
| 128 | + |
| 129 | +unsigned int MotionBlocks::pointInDirection(VirtualMachine *vm) |
| 130 | +{ |
| 131 | + Sprite *sprite = dynamic_cast<Sprite *>(vm->target()); |
| 132 | + |
| 133 | + if (sprite) |
| 134 | + sprite->setDirection(vm->getInput(0, 1)->toDouble()); |
| 135 | + |
| 136 | + return 1; |
| 137 | +} |
| 138 | + |
| 139 | +void MotionBlocks::pointTowardsPos(Sprite *sprite, double x, double y) |
| 140 | +{ |
| 141 | + if (!sprite) |
| 142 | + return; |
| 143 | + |
| 144 | + // https://en.scratch-wiki.info/wiki/Point_Towards_()_(block)#Workaround |
| 145 | + double deltaX = x - sprite->x(); |
| 146 | + double deltaY = y - sprite->y(); |
| 147 | + |
| 148 | + if (deltaY == 0) { |
| 149 | + if (deltaX < 0) |
| 150 | + sprite->setDirection(-90); |
| 151 | + else |
| 152 | + sprite->setDirection(90); |
| 153 | + } else if (deltaY < 0) |
| 154 | + sprite->setDirection(180 + (180 / pi) * std::atan(deltaX / deltaY)); |
| 155 | + else |
| 156 | + sprite->setDirection((180 / pi) * std::atan(deltaX / deltaY)); |
| 157 | +} |
| 158 | + |
| 159 | +unsigned int MotionBlocks::pointTowards(VirtualMachine *vm) |
| 160 | +{ |
| 161 | + std::string value = vm->getInput(0, 1)->toString(); |
| 162 | + Target *target; |
| 163 | + |
| 164 | + if (value == "_mouse_") |
| 165 | + pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY()); |
| 166 | + else if (value == "_random_") { |
| 167 | + // TODO: Read stage size from engine (#224) |
| 168 | + static const unsigned int stageWidth = 480; |
| 169 | + static const unsigned int stageHeight = 360; |
| 170 | + |
| 171 | + if (!rng) |
| 172 | + rng = RandomGenerator::instance().get(); |
| 173 | + |
| 174 | + pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2)); |
| 175 | + } else { |
| 176 | + target = vm->engine()->targetAt(vm->engine()->findTarget(value)); |
| 177 | + Sprite *sprite = dynamic_cast<Sprite *>(target); |
| 178 | + |
| 179 | + if (sprite) |
| 180 | + pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), sprite->x(), sprite->y()); |
| 181 | + } |
| 182 | + |
| 183 | + return 1; |
| 184 | +} |
| 185 | + |
| 186 | +unsigned int MotionBlocks::pointTowardsByIndex(VirtualMachine *vm) |
| 187 | +{ |
| 188 | + Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt()); |
| 189 | + Sprite *sprite = dynamic_cast<Sprite *>(target); |
| 190 | + |
| 191 | + if (sprite) |
| 192 | + pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), sprite->x(), sprite->y()); |
| 193 | + |
| 194 | + return 1; |
| 195 | +} |
| 196 | + |
| 197 | +unsigned int MotionBlocks::pointTowardsMousePointer(VirtualMachine *vm) |
| 198 | +{ |
| 199 | + pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY()); |
| 200 | + return 0; |
| 201 | +} |
| 202 | + |
| 203 | +unsigned int MotionBlocks::pointTowardsRandomPosition(VirtualMachine *vm) |
| 204 | +{ |
| 205 | + // TODO: Read stage size from engine (#224) |
| 206 | + static const unsigned int stageWidth = 480; |
| 207 | + static const unsigned int stageHeight = 360; |
| 208 | + |
| 209 | + if (!rng) |
| 210 | + rng = RandomGenerator::instance().get(); |
| 211 | + |
| 212 | + pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2)); |
| 213 | + |
| 214 | + return 0; |
| 215 | +} |
| 216 | + |
| 217 | +unsigned int MotionBlocks::goToXY(VirtualMachine *vm) |
| 218 | +{ |
| 219 | + Sprite *sprite = dynamic_cast<Sprite *>(vm->target()); |
| 220 | + |
| 221 | + if (sprite) { |
| 222 | + sprite->setX(vm->getInput(0, 2)->toDouble()); |
| 223 | + sprite->setY(vm->getInput(1, 2)->toDouble()); |
| 224 | + } |
| 225 | + |
| 226 | + return 2; |
14 | 227 | }
|
0 commit comments