|
9 | 9 | #include <scratchcpp/sprite.h> |
10 | 10 | #include <scratchcpp/stringptr.h> |
11 | 11 | #include <scratchcpp/value.h> |
| 12 | +#include <scratchcpp/input.h> |
12 | 13 | #include <scratchcpp/field.h> |
13 | 14 | #include <scratchcpp/igraphicseffect.h> |
14 | 15 | #include <scratchcpp/scratchconfiguration.h> |
@@ -47,6 +48,7 @@ void LooksBlocks::registerBlocks(IEngine *engine) |
47 | 48 | engine->addCompileFunction(this, "looks_changesizeby", &compileChangeSizeBy); |
48 | 49 | engine->addCompileFunction(this, "looks_setsizeto", &compileSetSizeTo); |
49 | 50 | engine->addCompileFunction(this, "looks_size", &compileSize); |
| 51 | + engine->addCompileFunction(this, "looks_switchcostumeto", &compileSwitchCostumeTo); |
50 | 52 | } |
51 | 53 |
|
52 | 54 | void LooksBlocks::onInit(IEngine *engine) |
@@ -202,6 +204,13 @@ CompilerValue *LooksBlocks::compileSize(Compiler *compiler) |
202 | 204 | return compiler->addTargetFunctionCall("looks_size", Compiler::StaticType::Number); |
203 | 205 | } |
204 | 206 |
|
| 207 | +CompilerValue *LooksBlocks::compileSwitchCostumeTo(Compiler *compiler) |
| 208 | +{ |
| 209 | + auto costume = compiler->addInput("COSTUME"); |
| 210 | + compiler->addTargetFunctionCall("looks_switchcostumeto", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { costume }); |
| 211 | + return nullptr; |
| 212 | +} |
| 213 | + |
205 | 214 | extern "C" void looks_start_stack_timer(ExecutionContext *ctx, double duration) |
206 | 215 | { |
207 | 216 | ctx->stackTimer()->start(duration); |
@@ -285,3 +294,59 @@ extern "C" double looks_size(Sprite *sprite) |
285 | 294 | { |
286 | 295 | return sprite->size(); |
287 | 296 | } |
| 297 | + |
| 298 | +extern "C" void looks_set_costume_by_index(Target *target, long index) |
| 299 | +{ |
| 300 | + const size_t costumeCount = target->costumes().size(); |
| 301 | + |
| 302 | + if (index < 0) |
| 303 | + index = (costumeCount + index % (-costumeCount)) % costumeCount; |
| 304 | + else if (index >= costumeCount) |
| 305 | + index = index % costumeCount; |
| 306 | + |
| 307 | + target->setCostumeIndex(index); |
| 308 | +} |
| 309 | + |
| 310 | +extern "C" void looks_nextcostume(Target *target) |
| 311 | +{ |
| 312 | + looks_set_costume_by_index(target, target->costumeIndex() + 1); |
| 313 | +} |
| 314 | + |
| 315 | +extern "C" void looks_previouscostume(Target *target) |
| 316 | +{ |
| 317 | + looks_set_costume_by_index(target, target->costumeIndex() - 1); |
| 318 | +} |
| 319 | + |
| 320 | +extern "C" void looks_switchcostumeto(Target *target, const ValueData *costume) |
| 321 | +{ |
| 322 | + // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/blocks/scratch3_looks.js#L389-L413 |
| 323 | + if (!value_isString(costume)) { |
| 324 | + // Numbers should be treated as costume indices, always |
| 325 | + if (value_isNaN(costume) || value_isInfinity(costume) || value_isNegativeInfinity(costume)) |
| 326 | + target->setCostumeIndex(0); |
| 327 | + else |
| 328 | + looks_set_costume_by_index(target, value_toLong(costume) - 1); |
| 329 | + } else { |
| 330 | + // Strings should be treated as costume names, where possible |
| 331 | + // TODO: Use UTF-16 in Target |
| 332 | + // StringPtr *nameStr = value_toStringPtr(costume); |
| 333 | + std::string nameStr; |
| 334 | + value_toString(costume, &nameStr); |
| 335 | + const int costumeIndex = target->findCostume(nameStr); |
| 336 | + |
| 337 | + auto it = std::find_if(nameStr.begin(), nameStr.end(), [](char c) { return !std::isspace(c); }); |
| 338 | + bool isWhiteSpace = (it == nameStr.end()); |
| 339 | + |
| 340 | + if (costumeIndex != -1) |
| 341 | + looks_set_costume_by_index(target, costumeIndex); |
| 342 | + else if (nameStr == "next costume") |
| 343 | + looks_nextcostume(target); |
| 344 | + else if (nameStr == "previous costume") { |
| 345 | + looks_previouscostume(target); |
| 346 | + // Try to cast the string to a number (and treat it as a costume index) |
| 347 | + // Pure whitespace should not be treated as a number |
| 348 | + // Note: isNaN will cast the string to a number before checking if it's NaN |
| 349 | + } else if (value_isValidNumber(costume) && !isWhiteSpace) |
| 350 | + looks_set_costume_by_index(target, value_toLong(costume) - 1); |
| 351 | + } |
| 352 | +} |
0 commit comments