Skip to content

Commit 5e9b560

Browse files
committed
Implement looks_changeeffectby block
1 parent 801eb9d commit 5e9b560

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed

src/blocks/looksblocks.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,21 @@
99
#include <scratchcpp/sprite.h>
1010
#include <scratchcpp/stringptr.h>
1111
#include <scratchcpp/value.h>
12+
#include <scratchcpp/field.h>
13+
#include <scratchcpp/igraphicseffect.h>
14+
#include <scratchcpp/scratchconfiguration.h>
1215
#include <utf8.h>
1316

1417
#include "looksblocks.h"
1518

1619
using namespace libscratchcpp;
1720

21+
LooksBlocks::~LooksBlocks()
22+
{
23+
if (m_engine)
24+
m_instances.erase(m_engine);
25+
}
26+
1827
std::string LooksBlocks::name() const
1928
{
2029
return "Looks";
@@ -38,6 +47,7 @@ void LooksBlocks::registerBlocks(IEngine *engine)
3847
engine->addCompileFunction(this, "looks_think", &compileThink);
3948
engine->addCompileFunction(this, "looks_show", &compileShow);
4049
engine->addCompileFunction(this, "looks_hide", &compileHide);
50+
engine->addCompileFunction(this, "looks_changeeffectby", &compileChangeEffectBy);
4151
}
4252

4353
void LooksBlocks::onInit(IEngine *engine)
@@ -63,6 +73,9 @@ void LooksBlocks::onInit(IEngine *engine)
6373
target->clearGraphicsEffects();
6474
}
6575
});
76+
77+
m_engine = engine;
78+
m_instances[engine] = this;
6679
}
6780

6881
void LooksBlocks::compileSayOrThinkForSecs(Compiler *compiler, const std::string function)
@@ -80,6 +93,30 @@ void LooksBlocks::compileSayOrThinkForSecs(Compiler *compiler, const std::string
8093
compiler->endLoop();
8194
}
8295

96+
long LooksBlocks::getEffectIndex(IEngine *engine, const std::string &name)
97+
{
98+
assert(engine);
99+
assert(m_instances.find(engine) != m_instances.cend());
100+
101+
LooksBlocks *instance = m_instances[engine];
102+
auto it = instance->m_effectMap.find(name);
103+
104+
if (it == instance->m_effectMap.cend()) {
105+
IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect(name);
106+
107+
if (effect) {
108+
instance->m_effects.push_back(effect);
109+
instance->m_effectMap[name] = instance->m_effects.size() - 1;
110+
return instance->m_effects.size() - 1;
111+
} else {
112+
std::cout << "warning: graphic effect '" << name << "' is not registered" << std::endl;
113+
return -1;
114+
}
115+
}
116+
117+
return it->second;
118+
}
119+
83120
CompilerValue *LooksBlocks::compileSayForSecs(Compiler *compiler)
84121
{
85122
compileSayOrThinkForSecs(compiler, "looks_say");
@@ -124,6 +161,24 @@ CompilerValue *LooksBlocks::compileHide(Compiler *compiler)
124161
return nullptr;
125162
}
126163

164+
CompilerValue *LooksBlocks::compileChangeEffectBy(Compiler *compiler)
165+
{
166+
Field *field = compiler->field("EFFECT");
167+
168+
if (!field)
169+
return nullptr;
170+
171+
auto index = getEffectIndex(compiler->engine(), field->value().toString());
172+
173+
if (index != -1) {
174+
auto indexValue = compiler->addConstValue(index);
175+
auto change = compiler->addInput("CHANGE");
176+
compiler->addTargetFunctionCall("looks_changeeffectby", Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { indexValue, change });
177+
}
178+
179+
return nullptr;
180+
}
181+
127182
extern "C" void looks_start_stack_timer(ExecutionContext *ctx, double duration)
128183
{
129184
ctx->stackTimer()->start(duration);
@@ -177,3 +232,9 @@ extern "C" void looks_hide(Sprite *sprite)
177232
{
178233
sprite->setVisible(false);
179234
}
235+
236+
extern "C" void looks_changeeffectby(Target *target, double index, double change)
237+
{
238+
IGraphicsEffect *effect = LooksBlocks::getEffect(target->engine(), index);
239+
target->setGraphicsEffectValue(effect, target->graphicsEffectValue(effect) + change);
240+
}

src/blocks/looksblocks.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,38 @@ namespace libscratchcpp
1111

1212
class Target;
1313
class Thread;
14+
class IGraphicsEffect;
1415

1516
class LooksBlocks : public IExtension
1617
{
1718
public:
19+
~LooksBlocks();
20+
1821
std::string name() const override;
1922
std::string description() const override;
2023
Rgb color() const override;
2124

2225
void registerBlocks(IEngine *engine) override;
2326
void onInit(IEngine *engine) override;
2427

28+
static inline IGraphicsEffect *getEffect(IEngine *engine, long index) { return m_instances[engine]->m_effects[index]; }
29+
2530
private:
2631
static void compileSayOrThinkForSecs(Compiler *compiler, const std::string function);
32+
static long getEffectIndex(IEngine *engine, const std::string &name);
33+
2734
static CompilerValue *compileSayForSecs(Compiler *compiler);
2835
static CompilerValue *compileSay(Compiler *compiler);
2936
static CompilerValue *compileThinkForSecs(Compiler *compiler);
3037
static CompilerValue *compileThink(Compiler *compiler);
3138
static CompilerValue *compileShow(Compiler *compiler);
3239
static CompilerValue *compileHide(Compiler *compiler);
40+
static CompilerValue *compileChangeEffectBy(Compiler *compiler);
41+
42+
IEngine *m_engine = nullptr;
43+
std::unordered_map<std::string, long> m_effectMap;
44+
std::vector<IGraphicsEffect *> m_effects;
45+
static inline std::unordered_map<IEngine *, LooksBlocks *> m_instances;
3346
};
3447

3548
} // namespace libscratchcpp

test/blocks/looks_blocks_test.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <scratchcpp/thread.h>
88
#include <scratchcpp/executablecode.h>
99
#include <scratchcpp/executioncontext.h>
10+
#include <scratchcpp/scratchconfiguration.h>
1011
#include <enginemock.h>
1112
#include <graphicseffectmock.h>
1213
#include <stacktimermock.h>
@@ -18,6 +19,8 @@ using namespace libscratchcpp;
1819
using namespace libscratchcpp::test;
1920

2021
using ::testing::Return;
22+
using ::testing::ReturnArg;
23+
using ::testing::_;
2124

2225
class LooksBlocksTest : public testing::Test
2326
{
@@ -28,6 +31,56 @@ class LooksBlocksTest : public testing::Test
2831
m_engine = m_project.engine().get();
2932
m_extension->registerBlocks(m_engine);
3033
m_extension->onInit(m_engine);
34+
35+
// Create and register fake graphic effects
36+
auto colorEffect = std::make_shared<GraphicsEffectMock>();
37+
auto fisheyeEffect = std::make_shared<GraphicsEffectMock>();
38+
auto whirlEffect = std::make_shared<GraphicsEffectMock>();
39+
auto pixelateEffect = std::make_shared<GraphicsEffectMock>();
40+
auto mosaicEffect = std::make_shared<GraphicsEffectMock>();
41+
auto brightnessEffect = std::make_shared<GraphicsEffectMock>();
42+
auto ghostEffect = std::make_shared<GraphicsEffectMock>();
43+
44+
EXPECT_CALL(*colorEffect, name()).WillOnce(Return("COLOR"));
45+
ScratchConfiguration::registerGraphicsEffect(colorEffect);
46+
47+
EXPECT_CALL(*fisheyeEffect, name()).WillOnce(Return("FISHEYE"));
48+
ScratchConfiguration::registerGraphicsEffect(fisheyeEffect);
49+
50+
EXPECT_CALL(*whirlEffect, name()).WillOnce(Return("WHIRL"));
51+
ScratchConfiguration::registerGraphicsEffect(whirlEffect);
52+
53+
EXPECT_CALL(*pixelateEffect, name()).WillOnce(Return("PIXELATE"));
54+
ScratchConfiguration::registerGraphicsEffect(pixelateEffect);
55+
56+
EXPECT_CALL(*mosaicEffect, name()).WillOnce(Return("MOSAIC"));
57+
ScratchConfiguration::registerGraphicsEffect(mosaicEffect);
58+
59+
EXPECT_CALL(*brightnessEffect, name()).WillOnce(Return("BRIGHTNESS"));
60+
ScratchConfiguration::registerGraphicsEffect(brightnessEffect);
61+
62+
EXPECT_CALL(*ghostEffect, name()).WillOnce(Return("GHOST"));
63+
ScratchConfiguration::registerGraphicsEffect(ghostEffect);
64+
65+
EXPECT_CALL(*colorEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
66+
EXPECT_CALL(*fisheyeEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
67+
EXPECT_CALL(*whirlEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
68+
EXPECT_CALL(*pixelateEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
69+
EXPECT_CALL(*mosaicEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
70+
EXPECT_CALL(*brightnessEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
71+
EXPECT_CALL(*ghostEffect, clamp(_)).WillRepeatedly(ReturnArg<0>());
72+
}
73+
74+
void TearDown() override
75+
{
76+
// Remove fake graphic effects
77+
ScratchConfiguration::removeGraphicsEffect("COLOR");
78+
ScratchConfiguration::removeGraphicsEffect("FISHEYE");
79+
ScratchConfiguration::removeGraphicsEffect("WHIRL");
80+
ScratchConfiguration::removeGraphicsEffect("PIXELATE");
81+
ScratchConfiguration::removeGraphicsEffect("MOSAIC");
82+
ScratchConfiguration::removeGraphicsEffect("BRIGHTNESS");
83+
ScratchConfiguration::removeGraphicsEffect("GHOST");
3184
}
3285

3386
std::unique_ptr<IExtension> m_extension;
@@ -260,3 +313,94 @@ TEST_F(LooksBlocksTest, Hide)
260313
builder.run();
261314
}
262315
}
316+
317+
TEST_F(LooksBlocksTest, ChangeEffectBy)
318+
{
319+
// Color
320+
{
321+
auto stage = std::make_shared<Stage>();
322+
stage->setEngine(m_engine);
323+
324+
ScriptBuilder builder(m_extension.get(), m_engine, stage);
325+
IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect("COLOR");
326+
ASSERT_TRUE(effect);
327+
328+
builder.addBlock("looks_changeeffectby");
329+
builder.addDropdownField("EFFECT", "COLOR");
330+
builder.addValueInput("CHANGE", 45.2);
331+
auto block = builder.currentBlock();
332+
333+
Compiler compiler(m_engine, stage.get());
334+
auto code = compiler.compile(block);
335+
Script script(stage.get(), block, m_engine);
336+
script.setCode(code);
337+
Thread thread(stage.get(), m_engine, &script);
338+
339+
stage->setGraphicsEffectValue(effect, 86.84);
340+
thread.run();
341+
ASSERT_EQ(std::round(stage->graphicsEffectValue(effect) * 100) / 100, 132.04);
342+
}
343+
344+
// Brightness
345+
{
346+
auto stage = std::make_shared<Stage>();
347+
stage->setEngine(m_engine);
348+
349+
ScriptBuilder builder(m_extension.get(), m_engine, stage);
350+
IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect("BRIGHTNESS");
351+
ASSERT_TRUE(effect);
352+
353+
builder.addBlock("looks_changeeffectby");
354+
builder.addDropdownField("EFFECT", "BRIGHTNESS");
355+
builder.addValueInput("CHANGE", 12.05);
356+
auto block = builder.currentBlock();
357+
358+
Compiler compiler(m_engine, stage.get());
359+
auto code = compiler.compile(block);
360+
Script script(stage.get(), block, m_engine);
361+
script.setCode(code);
362+
Thread thread(stage.get(), m_engine, &script);
363+
364+
thread.run();
365+
ASSERT_EQ(std::round(stage->graphicsEffectValue(effect) * 100) / 100, 12.05);
366+
}
367+
368+
// Ghost
369+
{
370+
auto stage = std::make_shared<Stage>();
371+
stage->setEngine(m_engine);
372+
373+
ScriptBuilder builder(m_extension.get(), m_engine, stage);
374+
IGraphicsEffect *effect = ScratchConfiguration::getGraphicsEffect("GHOST");
375+
ASSERT_TRUE(effect);
376+
377+
builder.addBlock("looks_changeeffectby");
378+
builder.addDropdownField("EFFECT", "GHOST");
379+
builder.addValueInput("CHANGE", -8.06);
380+
auto block = builder.currentBlock();
381+
382+
Compiler compiler(m_engine, stage.get());
383+
auto code = compiler.compile(block);
384+
Script script(stage.get(), block, m_engine);
385+
script.setCode(code);
386+
Thread thread(stage.get(), m_engine, &script);
387+
388+
stage->setGraphicsEffectValue(effect, 13.12);
389+
thread.run();
390+
ASSERT_EQ(std::round(stage->graphicsEffectValue(effect) * 100) / 100, 5.06);
391+
}
392+
393+
// Invalid
394+
{
395+
auto stage = std::make_shared<Stage>();
396+
stage->setEngine(m_engine);
397+
398+
ScriptBuilder builder(m_extension.get(), m_engine, stage);
399+
builder.addBlock("looks_changeeffectby");
400+
builder.addDropdownField("EFFECT", "INVALID");
401+
builder.addValueInput("CHANGE", 8.3);
402+
403+
builder.build();
404+
builder.run();
405+
}
406+
}

0 commit comments

Comments
 (0)