Skip to content

Commit 2ea6686

Browse files
committed
Implement event_whentouchingobject predicate
1 parent ce7cd5d commit 2ea6686

File tree

3 files changed

+144
-3
lines changed

3 files changed

+144
-3
lines changed

src/blocks/eventblocks.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
#include <scratchcpp/iengine.h>
44
#include <scratchcpp/compiler.h>
55
#include <scratchcpp/block.h>
6+
#include <scratchcpp/input.h>
67
#include <scratchcpp/field.h>
78
#include <scratchcpp/broadcast.h>
89
#include <scratchcpp/executioncontext.h>
910
#include <scratchcpp/thread.h>
1011
#include <scratchcpp/compilerconstant.h>
1112
#include <scratchcpp/promise.h>
1213
#include <scratchcpp/stringptr.h>
14+
#include <scratchcpp/sprite.h>
1315
#include <utf8.h>
1416

1517
#include "eventblocks.h"
@@ -33,6 +35,7 @@ Rgb EventBlocks::color() const
3335

3436
void EventBlocks::registerBlocks(IEngine *engine)
3537
{
38+
// Blocks
3639
engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject);
3740
engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked);
3841
engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked);
@@ -43,6 +46,9 @@ void EventBlocks::registerBlocks(IEngine *engine)
4346
engine->addCompileFunction(this, "event_broadcast", &compileBroadcast);
4447
engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait);
4548
engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed);
49+
50+
// Hat predicates
51+
engine->addHatPredicateCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObjectPredicate);
4652
}
4753

4854
CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler)
@@ -51,6 +57,21 @@ CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler)
5157
return nullptr;
5258
}
5359

60+
CompilerValue *EventBlocks::compileWhenTouchingObjectPredicate(Compiler *compiler)
61+
{
62+
Input *input = compiler->input("TOUCHINGOBJECTMENU");
63+
CompilerValue *name = nullptr;
64+
65+
if (input->pointsToDropdownMenu()) {
66+
std::string value = input->selectedMenuItem();
67+
68+
name = compiler->addConstValue(value);
69+
} else
70+
name = compiler->addInput(input);
71+
72+
return compiler->addTargetFunctionCall("event_whentouchingobject_predicate", Compiler::StaticType::Bool, { Compiler::StaticType::String }, { name });
73+
}
74+
5475
CompilerValue *EventBlocks::compileWhenFlagClicked(Compiler *compiler)
5576
{
5677
compiler->engine()->addGreenFlagScript(compiler->block());
@@ -127,6 +148,29 @@ CompilerValue *EventBlocks::compileWhenKeyPressed(Compiler *compiler)
127148
return nullptr;
128149
}
129150

151+
extern "C" bool event_whentouchingobject_predicate(Target *target, const StringPtr *name)
152+
{
153+
static const StringPtr MOUSE_STR = StringPtr("_mouse_");
154+
static const StringPtr EDGE_STR = StringPtr("_edge_");
155+
156+
IEngine *engine = target->engine();
157+
158+
if (string_compare_case_sensitive(name, &MOUSE_STR) == 0)
159+
return target->touchingPoint(engine->mouseX(), engine->mouseY());
160+
else if (string_compare_case_sensitive(name, &EDGE_STR) == 0)
161+
return target->touchingEdge();
162+
else {
163+
// TODO: Use UTF-16 in engine
164+
const std::string u8name = utf8::utf16to8(std::u16string(name->data));
165+
Target *anotherTarget = engine->targetAt(engine->findTarget(u8name));
166+
167+
if (anotherTarget && !anotherTarget->isStage())
168+
return target->touchingSprite(static_cast<Sprite *>(anotherTarget));
169+
else
170+
return false;
171+
}
172+
}
173+
130174
extern "C" void event_broadcast(ExecutionContext *ctx, const StringPtr *name, bool wait)
131175
{
132176
Thread *thread = ctx->thread();

src/blocks/eventblocks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class EventBlocks : public IExtension
1818

1919
private:
2020
static CompilerValue *compileWhenTouchingObject(Compiler *compiler);
21+
static CompilerValue *compileWhenTouchingObjectPredicate(Compiler *compiler);
2122
static CompilerValue *compileWhenFlagClicked(Compiler *compiler);
2223
static CompilerValue *compileWhenThisSpriteClicked(Compiler *compiler);
2324
static CompilerValue *compileWhenStageClicked(Compiler *compiler);

test/blocks/event_blocks_test.cpp

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <scratchcpp/thread.h>
99
#include <scratchcpp/executablecode.h>
1010
#include <enginemock.h>
11+
#include <targetmock.h>
1112

1213
#include "../common.h"
1314
#include "blocks/eventblocks.h"
@@ -33,12 +34,10 @@ class EventBlocksTest : public testing::Test
3334
EngineMock m_engineMock;
3435
};
3536

36-
// TODO: Add test for when touching object hat predicate
37-
3837
TEST_F(EventBlocksTest, WhenTouchingObject)
3938
{
4039
auto target = std::make_shared<Sprite>();
41-
ScriptBuilder builder(m_extension.get(), m_engine, target);
40+
ScriptBuilder builder(m_extension.get(), m_engine, target, false);
4241

4342
builder.addBlock("event_whentouchingobject");
4443
builder.addDropdownInput("TOUCHINGOBJECTMENU", "Sprite1");
@@ -49,6 +48,103 @@ TEST_F(EventBlocksTest, WhenTouchingObject)
4948
compiler.compile(block);
5049
}
5150

51+
TEST_F(EventBlocksTest, WhenTouchingObjectPredicate)
52+
{
53+
auto target = std::make_shared<TargetMock>();
54+
target->setEngine(&m_engineMock);
55+
Sprite sprite;
56+
57+
// "Sprite1"
58+
{
59+
ScriptBuilder builder(m_extension.get(), m_engine, target, false);
60+
builder.addBlock("event_whentouchingobject");
61+
builder.addDropdownInput("TOUCHINGOBJECTMENU", "Sprite1");
62+
auto block = builder.currentBlock();
63+
64+
Compiler compiler(&m_engineMock, target.get());
65+
auto code = compiler.compile(block, true);
66+
Script script(target.get(), block, &m_engineMock);
67+
script.setHatPredicateCode(code);
68+
Thread thread(target.get(), &m_engineMock, &script);
69+
70+
EXPECT_CALL(m_engineMock, findTarget("Sprite1")).WillOnce(Return(3));
71+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&sprite));
72+
EXPECT_CALL(*target, touchingClones).WillOnce(Return(false));
73+
ASSERT_FALSE(thread.runPredicate());
74+
75+
EXPECT_CALL(m_engineMock, findTarget("Sprite1")).WillOnce(Return(3));
76+
EXPECT_CALL(m_engineMock, targetAt(3)).WillOnce(Return(&sprite));
77+
EXPECT_CALL(*target, touchingClones).WillOnce(Return(true));
78+
ASSERT_TRUE(thread.runPredicate());
79+
}
80+
81+
// "_mouse_"
82+
{
83+
ScriptBuilder builder(m_extension.get(), m_engine, target, false);
84+
builder.addBlock("event_whentouchingobject");
85+
builder.addDropdownInput("TOUCHINGOBJECTMENU", "_mouse_");
86+
auto block = builder.currentBlock();
87+
88+
Compiler compiler(&m_engineMock, target.get());
89+
auto code = compiler.compile(block, true);
90+
Script script(target.get(), block, &m_engineMock);
91+
script.setHatPredicateCode(code);
92+
Thread thread(target.get(), &m_engineMock, &script);
93+
94+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(24.5));
95+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(-16.04));
96+
EXPECT_CALL(*target, touchingPoint(24.5, -16.04)).WillOnce(Return(false));
97+
ASSERT_FALSE(thread.runPredicate());
98+
99+
EXPECT_CALL(m_engineMock, mouseX()).WillOnce(Return(24.5));
100+
EXPECT_CALL(m_engineMock, mouseY()).WillOnce(Return(-16.04));
101+
EXPECT_CALL(*target, touchingPoint(24.5, -16.04)).WillOnce(Return(true));
102+
ASSERT_TRUE(thread.runPredicate());
103+
}
104+
105+
// "_edge_"
106+
{
107+
ScriptBuilder builder(m_extension.get(), m_engine, target, false);
108+
builder.addBlock("event_whentouchingobject");
109+
builder.addDropdownInput("TOUCHINGOBJECTMENU", "_edge_");
110+
auto block = builder.currentBlock();
111+
112+
Compiler compiler(&m_engineMock, target.get());
113+
auto code = compiler.compile(block, true);
114+
Script script(target.get(), block, &m_engineMock);
115+
script.setHatPredicateCode(code);
116+
Thread thread(target.get(), &m_engineMock, &script);
117+
118+
EXPECT_CALL(m_engineMock, stageWidth()).WillOnce(Return(10));
119+
EXPECT_CALL(m_engineMock, stageHeight()).WillOnce(Return(10));
120+
EXPECT_CALL(*target, boundingRect()).WillOnce(Return(Rect(-5, 5, 5, -5)));
121+
ASSERT_FALSE(thread.runPredicate());
122+
123+
EXPECT_CALL(m_engineMock, stageWidth()).WillOnce(Return(0));
124+
EXPECT_CALL(m_engineMock, stageHeight()).WillOnce(Return(0));
125+
EXPECT_CALL(*target, boundingRect()).WillOnce(Return(Rect(-5, 5, 5, -5)));
126+
ASSERT_TRUE(thread.runPredicate());
127+
}
128+
129+
// ""
130+
{
131+
ScriptBuilder builder(m_extension.get(), m_engine, target, false);
132+
builder.addBlock("event_whentouchingobject");
133+
builder.addDropdownInput("TOUCHINGOBJECTMENU", "");
134+
auto block = builder.currentBlock();
135+
136+
Compiler compiler(&m_engineMock, target.get());
137+
auto code = compiler.compile(block, true);
138+
Script script(target.get(), block, &m_engineMock);
139+
script.setHatPredicateCode(code);
140+
Thread thread(target.get(), &m_engineMock, &script);
141+
142+
EXPECT_CALL(m_engineMock, findTarget("")).WillOnce(Return(-1));
143+
EXPECT_CALL(m_engineMock, targetAt(-1)).WillOnce(Return(nullptr));
144+
ASSERT_FALSE(thread.runPredicate());
145+
}
146+
}
147+
52148
TEST_F(EventBlocksTest, WhenFlagClicked)
53149
{
54150
auto target = std::make_shared<Sprite>();

0 commit comments

Comments
 (0)