Skip to content

Commit 2083851

Browse files
authored
Merge pull request #377 from scratchcpp/sensing_of_variables
Add support for reading variables using the sensing_of block
2 parents 7901c1a + aac02f3 commit 2083851

File tree

3 files changed

+177
-6
lines changed

3 files changed

+177
-6
lines changed

src/blocks/sensingblocks.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <scratchcpp/sprite.h>
1010
#include <scratchcpp/stage.h>
1111
#include <scratchcpp/costume.h>
12+
#include <scratchcpp/variable.h>
1213
#include "sensingblocks.h"
1314

1415
#include "../engine/internal/clock.h"
@@ -141,15 +142,20 @@ void SensingBlocks::compileResetTimer(Compiler *compiler)
141142

142143
void SensingBlocks::compileOf(Compiler *compiler)
143144
{
144-
int option = compiler->field(PROPERTY)->specialValueId();
145+
Field *property = compiler->field(PROPERTY);
146+
assert(property);
147+
int option = property->specialValueId();
145148
Input *input = compiler->input(OBJECT);
149+
assert(input);
146150
BlockFunc f = nullptr;
147151

148152
if (input->type() != Input::Type::ObscuredShadow) {
149153
assert(input->pointsToDropdownMenu());
150154
std::string value = input->selectedMenuItem();
151155

152-
int index = compiler->engine()->findTarget(value);
156+
IEngine *engine = compiler->engine();
157+
assert(engine);
158+
int index = engine->findTarget(value);
153159

154160
switch (option) {
155161
case XPosition:
@@ -188,8 +194,18 @@ void SensingBlocks::compileOf(Compiler *compiler)
188194
f = &backdropNameOfStageByIndex;
189195
break;
190196

191-
default:
197+
default: {
198+
// Variable
199+
Target *target = engine->targetAt(index);
200+
auto varIndex = target->findVariable(property->value().toString());
201+
202+
if (varIndex == -1)
203+
compiler->addInstruction(vm::OP_NULL);
204+
else
205+
compiler->addInstruction(vm::OP_READ_VAR, { compiler->variableIndex(target->variableAt(varIndex)) });
206+
192207
break;
208+
}
193209
}
194210

195211
if (f)
@@ -233,6 +249,8 @@ void SensingBlocks::compileOf(Compiler *compiler)
233249
break;
234250

235251
default:
252+
f = &variableOfTarget;
253+
compiler->addConstValue(property->value().toString());
236254
break;
237255
}
238256

@@ -571,6 +589,23 @@ unsigned int SensingBlocks::volumeOfTargetByIndex(VirtualMachine *vm)
571589
return 0;
572590
}
573591

592+
unsigned int SensingBlocks::variableOfTarget(VirtualMachine *vm)
593+
{
594+
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(1, 2)->toString()));
595+
596+
if (target) {
597+
auto varIndex = target->findVariable(vm->getInput(0, 2)->toString());
598+
599+
if (varIndex == -1)
600+
vm->replaceReturnValue(0, 2);
601+
else
602+
vm->replaceReturnValue(target->variableAt(varIndex)->value(), 2);
603+
} else
604+
vm->replaceReturnValue(0, 2);
605+
606+
return 1;
607+
}
608+
574609
unsigned int SensingBlocks::backdropNumberOfStage(VirtualMachine *vm)
575610
{
576611
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(vm->getInput(0, 1)->toString()));

src/blocks/sensingblocks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class SensingBlocks : public IBlockSection
9393
static unsigned int sizeOfSpriteByIndex(VirtualMachine *vm);
9494
static unsigned int volumeOfTarget(VirtualMachine *vm);
9595
static unsigned int volumeOfTargetByIndex(VirtualMachine *vm);
96+
static unsigned int variableOfTarget(VirtualMachine *vm);
9697
static unsigned int backdropNumberOfStage(VirtualMachine *vm);
9798
static unsigned int backdropNumberOfStageByIndex(VirtualMachine *vm);
9899
static unsigned int backdropNameOfStage(VirtualMachine *vm);

test/blocks/sensing_blocks_test.cpp

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <scratchcpp/sprite.h>
66
#include <scratchcpp/stage.h>
77
#include <scratchcpp/costume.h>
8+
#include <scratchcpp/variable.h>
89
#include <enginemock.h>
910
#include <timermock.h>
1011
#include <clockmock.h>
@@ -706,10 +707,35 @@ TEST_F(SensingBlocksTest, Of)
706707
addDropdownField(block20, "PROPERTY", SensingBlocks::PROPERTY, "backdrop name", SensingBlocks::BackdropName);
707708
addDropdownInput(block20, "OBJECT", SensingBlocks::OBJECT, "", createNullBlock("ad"));
708709

710+
// [some variable] of (Stage)
711+
auto block21 = std::make_shared<Block>("ae", "sensing_of");
712+
addDropdownField(block21, "PROPERTY", SensingBlocks::PROPERTY, "some variable", static_cast<SensingBlocks::FieldValues>(-1));
713+
addDropdownInput(block21, "OBJECT", SensingBlocks::OBJECT, "_stage_");
714+
715+
// [some invalid variable] of (Stage)
716+
auto block22 = std::make_shared<Block>("af", "sensing_of");
717+
addDropdownField(block22, "PROPERTY", SensingBlocks::PROPERTY, "some invalid variable", static_cast<SensingBlocks::FieldValues>(-1));
718+
addDropdownInput(block22, "OBJECT", SensingBlocks::OBJECT, "_stage_");
719+
720+
// [some variable] of (Sprite2)
721+
auto block23 = std::make_shared<Block>("ag", "sensing_of");
722+
addDropdownField(block23, "PROPERTY", SensingBlocks::PROPERTY, "some variable", static_cast<SensingBlocks::FieldValues>(-1));
723+
addDropdownInput(block23, "OBJECT", SensingBlocks::OBJECT, "Sprite2");
724+
725+
// [some invalid variable] of (Sprite2)
726+
auto block24 = std::make_shared<Block>("ah", "sensing_of");
727+
addDropdownField(block24, "PROPERTY", SensingBlocks::PROPERTY, "some invalid variable", static_cast<SensingBlocks::FieldValues>(-1));
728+
addDropdownInput(block24, "OBJECT", SensingBlocks::OBJECT, "Sprite2");
729+
730+
// [some variable] of (null block)
731+
auto block25 = std::make_shared<Block>("ai", "sensing_of");
732+
addDropdownField(block25, "PROPERTY", SensingBlocks::PROPERTY, "some variable", static_cast<SensingBlocks::FieldValues>(-1));
733+
addDropdownInput(block25, "OBJECT", SensingBlocks::OBJECT, "", createNullBlock("aj"));
734+
709735
compiler.init();
710736

711-
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).Times(7).WillRepeatedly(Return(6));
712-
EXPECT_CALL(m_engineMock, findTarget("_stage_")).Times(3).WillRepeatedly(Return(0));
737+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).Times(9).WillRepeatedly(Return(6));
738+
EXPECT_CALL(m_engineMock, findTarget("_stage_")).Times(5).WillRepeatedly(Return(0));
713739

714740
EXPECT_CALL(m_engineMock, functionIndex(&SensingBlocks::xPositionOfSpriteByIndex)).WillOnce(Return(0));
715741
compiler.setBlock(block1);
@@ -791,6 +817,34 @@ TEST_F(SensingBlocksTest, Of)
791817
compiler.setBlock(block20);
792818
SensingBlocks::compileOf(&compiler);
793819

820+
Stage stage;
821+
auto v1 = std::make_shared<Variable>("var1", "test");
822+
auto v2 = std::make_shared<Variable>("var2", "some variable");
823+
stage.addVariable(v1);
824+
stage.addVariable(v2);
825+
EXPECT_CALL(m_engineMock, targetAt(0)).WillOnce(Return(&stage));
826+
compiler.setBlock(block21);
827+
SensingBlocks::compileOf(&compiler);
828+
829+
EXPECT_CALL(m_engineMock, targetAt(0)).WillOnce(Return(&stage));
830+
compiler.setBlock(block22);
831+
SensingBlocks::compileOf(&compiler);
832+
833+
Sprite sprite;
834+
auto v3 = std::make_shared<Variable>("var3", "some variable");
835+
sprite.addVariable(v3);
836+
EXPECT_CALL(m_engineMock, targetAt(6)).WillOnce(Return(&sprite));
837+
compiler.setBlock(block23);
838+
SensingBlocks::compileOf(&compiler);
839+
840+
EXPECT_CALL(m_engineMock, targetAt(6)).WillOnce(Return(&sprite));
841+
compiler.setBlock(block24);
842+
SensingBlocks::compileOf(&compiler);
843+
844+
EXPECT_CALL(m_engineMock, functionIndex(&SensingBlocks::variableOfTarget)).WillOnce(Return(18));
845+
compiler.setBlock(block25);
846+
SensingBlocks::compileOf(&compiler);
847+
794848
compiler.end();
795849

796850
ASSERT_EQ(
@@ -867,8 +921,20 @@ TEST_F(SensingBlocksTest, Of)
867921
vm::OP_NULL,
868922
vm::OP_EXEC,
869923
17,
924+
vm::OP_READ_VAR,
925+
0,
926+
vm::OP_NULL,
927+
vm::OP_READ_VAR,
928+
1,
929+
vm::OP_NULL,
930+
vm::OP_CONST,
931+
10,
932+
vm::OP_NULL,
933+
vm::OP_EXEC,
934+
18,
870935
vm::OP_HALT }));
871-
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 6, 6, 6, 6, 6, 6, 6, 0, 0, 0 }));
936+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, "some variable" }));
937+
ASSERT_EQ(compiler.variables(), std::vector<Variable *>({ v2.get(), v3.get() }));
872938
}
873939

874940
TEST_F(SensingBlocksTest, XPositionOfSprite)
@@ -1114,6 +1180,75 @@ TEST_F(SensingBlocksTest, VolumeOfTarget)
11141180
ASSERT_EQ(vm.getInput(0, 1)->toDouble(), stage.volume());
11151181
}
11161182

1183+
TEST_F(SensingBlocksTest, VariableOfTarget)
1184+
{
1185+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT };
1186+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT };
1187+
static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 3, vm::OP_EXEC, 0, vm::OP_HALT };
1188+
static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 3, vm::OP_EXEC, 0, vm::OP_HALT };
1189+
static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 4, vm::OP_EXEC, 0, vm::OP_HALT };
1190+
static BlockFunc functions[] = { &SensingBlocks::variableOfTarget };
1191+
static Value constValues[] = { "variable", "invalid variable", "Sprite2", "_stage_", "test" };
1192+
1193+
Sprite sprite;
1194+
auto v1 = std::make_shared<Variable>("var1", "variable", 64.13);
1195+
sprite.addVariable(v1);
1196+
1197+
Stage stage;
1198+
auto v2 = std::make_shared<Variable>("var2", "test", 98);
1199+
auto v3 = std::make_shared<Variable>("var3", "variable", -0.85);
1200+
stage.addVariable(v2);
1201+
stage.addVariable(v3);
1202+
1203+
VirtualMachine vm(nullptr, &m_engineMock, nullptr);
1204+
vm.setFunctions(functions);
1205+
vm.setConstValues(constValues);
1206+
1207+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(6));
1208+
EXPECT_CALL(m_engineMock, targetAt(6)).WillOnce(Return(&sprite));
1209+
vm.setBytecode(bytecode1);
1210+
vm.run();
1211+
1212+
ASSERT_EQ(vm.registerCount(), 1);
1213+
ASSERT_EQ(vm.getInput(0, 1)->toDouble(), v1->value().toDouble());
1214+
1215+
EXPECT_CALL(m_engineMock, findTarget("Sprite2")).WillOnce(Return(6));
1216+
EXPECT_CALL(m_engineMock, targetAt(6)).WillOnce(Return(&sprite));
1217+
vm.reset();
1218+
vm.setBytecode(bytecode2);
1219+
vm.run();
1220+
1221+
ASSERT_EQ(vm.registerCount(), 1);
1222+
ASSERT_EQ(vm.getInput(0, 1)->toDouble(), 0);
1223+
1224+
EXPECT_CALL(m_engineMock, findTarget("_stage_")).WillOnce(Return(0));
1225+
EXPECT_CALL(m_engineMock, targetAt(0)).WillOnce(Return(&stage));
1226+
vm.reset();
1227+
vm.setBytecode(bytecode3);
1228+
vm.run();
1229+
1230+
ASSERT_EQ(vm.registerCount(), 1);
1231+
ASSERT_EQ(vm.getInput(0, 1)->toDouble(), v3->value().toDouble());
1232+
1233+
EXPECT_CALL(m_engineMock, findTarget("_stage_")).WillOnce(Return(0));
1234+
EXPECT_CALL(m_engineMock, targetAt(0)).WillOnce(Return(&stage));
1235+
vm.reset();
1236+
vm.setBytecode(bytecode4);
1237+
vm.run();
1238+
1239+
ASSERT_EQ(vm.registerCount(), 1);
1240+
ASSERT_EQ(vm.getInput(0, 1)->toDouble(), 0);
1241+
1242+
EXPECT_CALL(m_engineMock, findTarget("test")).WillOnce(Return(-1));
1243+
EXPECT_CALL(m_engineMock, targetAt(-1)).WillOnce(Return(nullptr));
1244+
vm.reset();
1245+
vm.setBytecode(bytecode5);
1246+
vm.run();
1247+
1248+
ASSERT_EQ(vm.registerCount(), 1);
1249+
ASSERT_EQ(vm.getInput(0, 1)->toDouble(), 0);
1250+
}
1251+
11171252
TEST_F(SensingBlocksTest, BackdropNumberOfStage)
11181253
{
11191254
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };

0 commit comments

Comments
 (0)