Skip to content

Commit d9a7b76

Browse files
authored
Merge pull request #544 from scratchcpp/when_touching_object_block
Implement the when touching object block
2 parents e922390 + 9c80d41 commit d9a7b76

File tree

11 files changed

+312
-55
lines changed

11 files changed

+312
-55
lines changed

include/scratchcpp/iengine.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
297297
/*! Returns the index of the broadcast with the given ID. */
298298
virtual int findBroadcastById(const std::string &broadcastId) const = 0;
299299

300+
/* Registers the given "when touching object" script. */
301+
virtual void addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock) = 0;
302+
300303
/*! Registers the "green flag" script. */
301304
virtual void addGreenFlagScript(std::shared_ptr<Block> hatBlock) = 0;
302305

include/scratchcpp/script.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class LIBSCRATCHCPP_EXPORT Script
3535
void setBytecode(const std::vector<unsigned int> &code);
3636

3737
void setHatPredicateBytecode(const std::vector<unsigned int> &code);
38-
bool runHatPredicate();
38+
bool runHatPredicate(Target *target);
3939

4040
void setProcedures(const std::vector<unsigned int *> &procedures);
4141
void setConstValues(const std::vector<Value> &values);

src/blocks/eventblocks.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <scratchcpp/input.h>
77
#include <scratchcpp/inputvalue.h>
88
#include <scratchcpp/field.h>
9+
#include <scratchcpp/sprite.h>
910
#include <scratchcpp/stage.h>
1011
#include <scratchcpp/costume.h>
1112
#include <scratchcpp/block.h>
@@ -27,6 +28,7 @@ std::string EventBlocks::name() const
2728
void EventBlocks::registerBlocks(IEngine *engine)
2829
{
2930
// Blocks
31+
engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject);
3032
engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked);
3133
engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked);
3234
engine->addCompileFunction(this, "event_whenstageclicked", &compileWhenStageClicked);
@@ -38,9 +40,11 @@ void EventBlocks::registerBlocks(IEngine *engine)
3840
engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed);
3941

4042
// Hat predicates
43+
engine->addHatPredicateCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObjectPredicate);
4144
engine->addHatPredicateCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThanPredicate);
4245

4346
// Inputs
47+
engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU);
4448
engine->addInput(this, "BROADCAST_INPUT", BROADCAST_INPUT);
4549
engine->addInput(this, "VALUE", VALUE);
4650

@@ -55,6 +59,27 @@ void EventBlocks::registerBlocks(IEngine *engine)
5559
engine->addFieldValue(this, "TIMER", Timer);
5660
}
5761

62+
void EventBlocks::compileWhenTouchingObjectPredicate(Compiler *compiler)
63+
{
64+
Input *input = compiler->input(TOUCHINGOBJECTMENU);
65+
66+
if (input->type() != Input::Type::ObscuredShadow) {
67+
assert(input->pointsToDropdownMenu());
68+
std::string value = input->selectedMenuItem();
69+
70+
compiler->addConstValue(value);
71+
compiler->addFunctionCall(&whenTouchingObjectPredicate);
72+
} else {
73+
compiler->addInput(input);
74+
compiler->addFunctionCall(&whenTouchingObjectPredicate);
75+
}
76+
}
77+
78+
void EventBlocks::compileWhenTouchingObject(Compiler *compiler)
79+
{
80+
compiler->engine()->addWhenTouchingObjectScript(compiler->block());
81+
}
82+
5883
void EventBlocks::compileWhenFlagClicked(Compiler *compiler)
5984
{
6085
compiler->engine()->addGreenFlagScript(compiler->block());
@@ -151,6 +176,26 @@ void EventBlocks::compileWhenKeyPressed(Compiler *compiler)
151176
compiler->engine()->addKeyPressScript(compiler->block(), KEY_OPTION);
152177
}
153178

179+
unsigned int EventBlocks::whenTouchingObjectPredicate(VirtualMachine *vm)
180+
{
181+
std::string value = vm->getInput(0, 1)->toString();
182+
183+
if (value == "_mouse_")
184+
vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1);
185+
else if (value == "_edge_")
186+
vm->replaceReturnValue(vm->target()->touchingEdge(), 1);
187+
else {
188+
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value));
189+
190+
if (target && !target->isStage())
191+
vm->replaceReturnValue(vm->target()->touchingSprite(static_cast<Sprite *>(target)), 1);
192+
else
193+
vm->replaceReturnValue(false, 1);
194+
}
195+
196+
return 0;
197+
}
198+
154199
unsigned int EventBlocks::broadcast(VirtualMachine *vm)
155200
{
156201
vm->engine()->broadcast(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString()));

src/blocks/eventblocks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class EventBlocks : public IBlockSection
1717
public:
1818
enum Inputs
1919
{
20+
TOUCHINGOBJECTMENU,
2021
BROADCAST_INPUT,
2122
VALUE
2223
};
@@ -39,6 +40,8 @@ class EventBlocks : public IBlockSection
3940

4041
void registerBlocks(IEngine *engine) override;
4142

43+
static void compileWhenTouchingObjectPredicate(Compiler *compiler);
44+
static void compileWhenTouchingObject(Compiler *compiler);
4245
static void compileWhenFlagClicked(Compiler *compiler);
4346
static void compileWhenThisSpriteClicked(Compiler *compiler);
4447
static void compileWhenStageClicked(Compiler *compiler);
@@ -50,6 +53,8 @@ class EventBlocks : public IBlockSection
5053
static void compileWhenGreaterThan(Compiler *compiler);
5154
static void compileWhenKeyPressed(Compiler *compiler);
5255

56+
static unsigned int whenTouchingObjectPredicate(VirtualMachine *vm);
57+
5358
static unsigned int broadcast(VirtualMachine *vm);
5459
static unsigned int broadcastByIndex(VirtualMachine *vm);
5560
static unsigned int broadcastAndWait(VirtualMachine *vm);

src/engine/internal/engine.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
using namespace libscratchcpp;
3636

3737
const std::unordered_map<Engine::HatType, bool> Engine::m_hatRestartExistingThreads = {
38-
{ HatType::GreenFlag, true }, { HatType::BroadcastReceived, true }, { HatType::BackdropChanged, true }, { HatType::CloneInit, false },
39-
{ HatType::KeyPressed, false }, { HatType::TargetClicked, true }, { HatType::WhenGreaterThan, false }
38+
{ HatType::WhenTouchingObject, false }, { HatType::GreenFlag, true }, { HatType::BroadcastReceived, true }, { HatType::BackdropChanged, true },
39+
{ HatType::CloneInit, false }, { HatType::KeyPressed, false }, { HatType::TargetClicked, true }, { HatType::WhenGreaterThan, false }
4040
};
4141

4242
const std::unordered_map<Engine::HatType, bool> Engine::m_hatEdgeActivated = {
43-
{ HatType::GreenFlag, false }, { HatType::BroadcastReceived, false }, { HatType::BackdropChanged, false }, { HatType::CloneInit, false },
44-
{ HatType::KeyPressed, false }, { HatType::TargetClicked, false }, { HatType::WhenGreaterThan, true }
43+
{ HatType::WhenTouchingObject, true }, { HatType::GreenFlag, false }, { HatType::BroadcastReceived, false }, { HatType::BackdropChanged, false },
44+
{ HatType::CloneInit, false }, { HatType::KeyPressed, false }, { HatType::TargetClicked, false }, { HatType::WhenGreaterThan, true }
4545
};
4646

4747
Engine::Engine() :
@@ -82,6 +82,7 @@ void Engine::clear()
8282
m_scripts.clear();
8383
m_functions.clear();
8484

85+
m_whenTouchingObjectHats.clear();
8586
m_greenFlagHats.clear();
8687
m_backdropChangeHats.clear();
8788
m_broadcastHats.clear();
@@ -516,24 +517,24 @@ void Engine::step()
516517
bool oldValue = false;
517518
auto hatBlock = thread->script()->topBlock();
518519
assert(hatBlock);
519-
assert(hatBlock->fieldAt(0)); // TODO: Edge-activated hats currently support only one field
520-
int fieldValueId = hatBlock->fieldAt(0)->specialValueId();
521-
assert(fieldValueId != -1);
522-
auto it = m_edgeActivatedHatValues.find(hatType);
520+
521+
Target *target = hatBlock->target();
522+
assert(target);
523+
auto it = m_edgeActivatedHatValues.find(hatBlock.get());
523524

524525
if (it == m_edgeActivatedHatValues.cend()) {
525-
m_edgeActivatedHatValues[hatType] = {};
526+
m_edgeActivatedHatValues[hatBlock.get()] = {};
526527
} else {
527-
const std::unordered_map<int, bool> &values = it->second;
528-
auto fieldIt = values.find(fieldValueId);
528+
auto &map = it->second;
529+
auto it = map.find(target);
529530

530-
if (fieldIt != values.cend())
531-
oldValue = fieldIt->second;
531+
if (it != map.cend())
532+
oldValue = it->second;
532533
}
533534

534-
bool newValue = thread->script()->runHatPredicate();
535+
bool newValue = thread->script()->runHatPredicate(hatBlock->target());
535536
bool edgeWasActivated = !oldValue && newValue; // changed from false true
536-
m_edgeActivatedHatValues[hatType][fieldValueId] = newValue;
537+
m_edgeActivatedHatValues[hatBlock.get()][target] = newValue;
537538

538539
if (!edgeWasActivated)
539540
stopThread(thread.get());
@@ -1080,6 +1081,11 @@ int Engine::findBroadcastById(const std::string &broadcastId) const
10801081
return it - m_broadcasts.begin();
10811082
}
10821083

1084+
void Engine::addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock)
1085+
{
1086+
addHatToMap(m_whenTouchingObjectHats, m_scripts[hatBlock].get());
1087+
}
1088+
10831089
void Engine::addGreenFlagScript(std::shared_ptr<Block> hatBlock)
10841090
{
10851091
addHatToMap(m_greenFlagHats, m_scripts[hatBlock].get());
@@ -1511,6 +1517,9 @@ const std::vector<Script *> &Engine::getHats(Target *target, HatType type)
15111517
}
15121518

15131519
switch (type) {
1520+
case HatType::WhenTouchingObject:
1521+
return m_whenTouchingObjectHats[target];
1522+
15141523
case HatType::GreenFlag:
15151524
return m_greenFlagHats[target];
15161525

src/engine/internal/engine.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class Engine : public IEngine
126126
int findBroadcast(const std::string &broadcastName) const override;
127127
int findBroadcastById(const std::string &broadcastId) const override;
128128

129+
void addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock) override;
129130
void addGreenFlagScript(std::shared_ptr<Block> hatBlock) override;
130131
void addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, int fieldId, Broadcast *broadcast) override;
131132
void addBackdropChangeScript(std::shared_ptr<Block> hatBlock, int fieldId) override;
@@ -172,6 +173,7 @@ class Engine : public IEngine
172173
private:
173174
enum class HatType
174175
{
176+
WhenTouchingObject,
175177
GreenFlag,
176178
BroadcastReceived,
177179
BackdropChanged,
@@ -242,6 +244,7 @@ class Engine : public IEngine
242244
std::recursive_mutex m_eventLoopMutex;
243245
std::string m_userAgent;
244246

247+
std::unordered_map<Target *, std::vector<Script *>> m_whenTouchingObjectHats;
245248
std::unordered_map<Target *, std::vector<Script *>> m_greenFlagHats;
246249
std::unordered_map<Target *, std::vector<Script *>> m_backdropChangeHats;
247250
std::unordered_map<Target *, std::vector<Script *>> m_broadcastHats;
@@ -252,7 +255,7 @@ class Engine : public IEngine
252255

253256
std::unordered_map<Script *, std::unordered_map<HatField, int>> m_scriptHatFields; // HatField, field ID from the block implementation
254257

255-
std::unordered_map<HatType, std::unordered_map<int, bool>> m_edgeActivatedHatValues; // (field value, last value) edge-activated hats only run after the value changes from false to true
258+
std::unordered_map<Block *, std::unordered_map<Target *, bool>> m_edgeActivatedHatValues; // (block, target, last value) edge-activated hats only run after the value changes from false to true
256259

257260
std::unique_ptr<ITimer> m_defaultTimer;
258261
ITimer *m_timer = nullptr;

src/engine/script.cpp

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,29 +53,28 @@ void Script::setBytecode(const std::vector<unsigned int> &code)
5353
void Script::setHatPredicateBytecode(const std::vector<unsigned int> &code)
5454
{
5555
impl->hatPredicateBytecodeVector = code;
56-
57-
if (impl->engine && !code.empty()) {
58-
impl->hatPredicateVm = std::make_shared<VirtualMachine>(impl->engine->stage(), impl->engine, this);
59-
impl->hatPredicateVm->setBytecode(impl->hatPredicateBytecodeVector.data());
60-
impl->hatPredicateVm->setConstValues(impl->constValues);
61-
}
6256
}
6357

6458
/*!
65-
* Runs the edge-activated hat predicate and returns the reported value.
59+
* Runs the edge-activated hat predicate as the given target and returns the reported value.
6660
* \note If there isn't any predicate, nothing will happen and the returned value will be false.
6761
*/
68-
bool Script::runHatPredicate()
62+
bool Script::runHatPredicate(Target *target)
6963
{
70-
if (impl->hatPredicateVm && impl->hatPredicateVm->bytecode()) {
71-
impl->hatPredicateVm->reset();
72-
impl->hatPredicateVm->setFunctions(getFunctions());
73-
impl->hatPredicateVm->run();
74-
assert(impl->hatPredicateVm->registerCount() == 1);
75-
76-
if (impl->hatPredicateVm->registerCount() == 1)
77-
return impl->hatPredicateVm->getInput(0, 1)->toBool();
78-
}
64+
if (!target || !impl->engine || impl->hatPredicateBytecodeVector.empty())
65+
return false;
66+
67+
auto vm = std::make_shared<VirtualMachine>(target, impl->engine, this);
68+
vm->setBytecode(impl->hatPredicateBytecodeVector.data());
69+
vm->setConstValues(impl->constValues);
70+
vm->setFunctions(getFunctions());
71+
vm->setVariables(impl->variableValues.data());
72+
vm->setLists(impl->lists.data());
73+
vm->run();
74+
assert(vm->registerCount() == 1);
75+
76+
if (vm->registerCount() == 1)
77+
return vm->getInput(0, 1)->toBool();
7978

8079
return false;
8180
}
@@ -161,9 +160,6 @@ void Script::setConstValues(const std::vector<Value> &values)
161160
{
162161
impl->constValuesVector = values;
163162
impl->constValues = impl->constValuesVector.data();
164-
165-
if (impl->hatPredicateVm)
166-
impl->hatPredicateVm->setConstValues(impl->constValues);
167163
}
168164

169165
/*! Sets the list of variables. */

src/engine/script_p.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ struct ScriptPrivate
2222

2323
unsigned int *bytecode = nullptr;
2424
std::vector<unsigned int> bytecodeVector;
25-
2625
std::vector<unsigned int> hatPredicateBytecodeVector;
27-
std::shared_ptr<VirtualMachine> hatPredicateVm;
2826

2927
Target *target = nullptr;
3028
std::shared_ptr<Block> topBlock;

0 commit comments

Comments
 (0)