Skip to content

Implement the when touching object block #544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Returns the index of the broadcast with the given ID. */
virtual int findBroadcastById(const std::string &broadcastId) const = 0;

/* Registers the given "when touching object" script. */
virtual void addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock) = 0;

/*! Registers the "green flag" script. */
virtual void addGreenFlagScript(std::shared_ptr<Block> hatBlock) = 0;

Expand Down
2 changes: 1 addition & 1 deletion include/scratchcpp/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class LIBSCRATCHCPP_EXPORT Script
void setBytecode(const std::vector<unsigned int> &code);

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

void setProcedures(const std::vector<unsigned int *> &procedures);
void setConstValues(const std::vector<Value> &values);
Expand Down
45 changes: 45 additions & 0 deletions src/blocks/eventblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <scratchcpp/input.h>
#include <scratchcpp/inputvalue.h>
#include <scratchcpp/field.h>
#include <scratchcpp/sprite.h>
#include <scratchcpp/stage.h>
#include <scratchcpp/costume.h>
#include <scratchcpp/block.h>
Expand All @@ -27,6 +28,7 @@ std::string EventBlocks::name() const
void EventBlocks::registerBlocks(IEngine *engine)
{
// Blocks
engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject);
engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked);
engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked);
engine->addCompileFunction(this, "event_whenstageclicked", &compileWhenStageClicked);
Expand All @@ -38,9 +40,11 @@ void EventBlocks::registerBlocks(IEngine *engine)
engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed);

// Hat predicates
engine->addHatPredicateCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObjectPredicate);
engine->addHatPredicateCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThanPredicate);

// Inputs
engine->addInput(this, "TOUCHINGOBJECTMENU", TOUCHINGOBJECTMENU);
engine->addInput(this, "BROADCAST_INPUT", BROADCAST_INPUT);
engine->addInput(this, "VALUE", VALUE);

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

void EventBlocks::compileWhenTouchingObjectPredicate(Compiler *compiler)
{
Input *input = compiler->input(TOUCHINGOBJECTMENU);

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

compiler->addConstValue(value);
compiler->addFunctionCall(&whenTouchingObjectPredicate);
} else {
compiler->addInput(input);
compiler->addFunctionCall(&whenTouchingObjectPredicate);
}
}

void EventBlocks::compileWhenTouchingObject(Compiler *compiler)
{
compiler->engine()->addWhenTouchingObjectScript(compiler->block());
}

void EventBlocks::compileWhenFlagClicked(Compiler *compiler)
{
compiler->engine()->addGreenFlagScript(compiler->block());
Expand Down Expand Up @@ -151,6 +176,26 @@ void EventBlocks::compileWhenKeyPressed(Compiler *compiler)
compiler->engine()->addKeyPressScript(compiler->block(), KEY_OPTION);
}

unsigned int EventBlocks::whenTouchingObjectPredicate(VirtualMachine *vm)
{
std::string value = vm->getInput(0, 1)->toString();

if (value == "_mouse_")
vm->replaceReturnValue(vm->target()->touchingPoint(vm->engine()->mouseX(), vm->engine()->mouseY()), 1);
else if (value == "_edge_")
vm->replaceReturnValue(vm->target()->touchingEdge(), 1);
else {
Target *target = vm->engine()->targetAt(vm->engine()->findTarget(value));

if (target && !target->isStage())
vm->replaceReturnValue(vm->target()->touchingSprite(static_cast<Sprite *>(target)), 1);
else
vm->replaceReturnValue(false, 1);
}

return 0;
}

unsigned int EventBlocks::broadcast(VirtualMachine *vm)
{
vm->engine()->broadcast(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString()));
Expand Down
5 changes: 5 additions & 0 deletions src/blocks/eventblocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class EventBlocks : public IBlockSection
public:
enum Inputs
{
TOUCHINGOBJECTMENU,
BROADCAST_INPUT,
VALUE
};
Expand All @@ -39,6 +40,8 @@ class EventBlocks : public IBlockSection

void registerBlocks(IEngine *engine) override;

static void compileWhenTouchingObjectPredicate(Compiler *compiler);
static void compileWhenTouchingObject(Compiler *compiler);
static void compileWhenFlagClicked(Compiler *compiler);
static void compileWhenThisSpriteClicked(Compiler *compiler);
static void compileWhenStageClicked(Compiler *compiler);
Expand All @@ -50,6 +53,8 @@ class EventBlocks : public IBlockSection
static void compileWhenGreaterThan(Compiler *compiler);
static void compileWhenKeyPressed(Compiler *compiler);

static unsigned int whenTouchingObjectPredicate(VirtualMachine *vm);

static unsigned int broadcast(VirtualMachine *vm);
static unsigned int broadcastByIndex(VirtualMachine *vm);
static unsigned int broadcastAndWait(VirtualMachine *vm);
Expand Down
39 changes: 24 additions & 15 deletions src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
using namespace libscratchcpp;

const std::unordered_map<Engine::HatType, bool> Engine::m_hatRestartExistingThreads = {
{ HatType::GreenFlag, true }, { HatType::BroadcastReceived, true }, { HatType::BackdropChanged, true }, { HatType::CloneInit, false },
{ HatType::KeyPressed, false }, { HatType::TargetClicked, true }, { HatType::WhenGreaterThan, false }
{ HatType::WhenTouchingObject, false }, { HatType::GreenFlag, true }, { HatType::BroadcastReceived, true }, { HatType::BackdropChanged, true },
{ HatType::CloneInit, false }, { HatType::KeyPressed, false }, { HatType::TargetClicked, true }, { HatType::WhenGreaterThan, false }
};

const std::unordered_map<Engine::HatType, bool> Engine::m_hatEdgeActivated = {
{ HatType::GreenFlag, false }, { HatType::BroadcastReceived, false }, { HatType::BackdropChanged, false }, { HatType::CloneInit, false },
{ HatType::KeyPressed, false }, { HatType::TargetClicked, false }, { HatType::WhenGreaterThan, true }
{ HatType::WhenTouchingObject, true }, { HatType::GreenFlag, false }, { HatType::BroadcastReceived, false }, { HatType::BackdropChanged, false },
{ HatType::CloneInit, false }, { HatType::KeyPressed, false }, { HatType::TargetClicked, false }, { HatType::WhenGreaterThan, true }
};

Engine::Engine() :
Expand Down Expand Up @@ -82,6 +82,7 @@ void Engine::clear()
m_scripts.clear();
m_functions.clear();

m_whenTouchingObjectHats.clear();
m_greenFlagHats.clear();
m_backdropChangeHats.clear();
m_broadcastHats.clear();
Expand Down Expand Up @@ -516,24 +517,24 @@ void Engine::step()
bool oldValue = false;
auto hatBlock = thread->script()->topBlock();
assert(hatBlock);
assert(hatBlock->fieldAt(0)); // TODO: Edge-activated hats currently support only one field
int fieldValueId = hatBlock->fieldAt(0)->specialValueId();
assert(fieldValueId != -1);
auto it = m_edgeActivatedHatValues.find(hatType);

Target *target = hatBlock->target();
assert(target);
auto it = m_edgeActivatedHatValues.find(hatBlock.get());

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

if (fieldIt != values.cend())
oldValue = fieldIt->second;
if (it != map.cend())
oldValue = it->second;
}

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

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

void Engine::addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock)
{
addHatToMap(m_whenTouchingObjectHats, m_scripts[hatBlock].get());
}

void Engine::addGreenFlagScript(std::shared_ptr<Block> hatBlock)
{
addHatToMap(m_greenFlagHats, m_scripts[hatBlock].get());
Expand Down Expand Up @@ -1511,6 +1517,9 @@ const std::vector<Script *> &Engine::getHats(Target *target, HatType type)
}

switch (type) {
case HatType::WhenTouchingObject:
return m_whenTouchingObjectHats[target];

case HatType::GreenFlag:
return m_greenFlagHats[target];

Expand Down
5 changes: 4 additions & 1 deletion src/engine/internal/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class Engine : public IEngine
int findBroadcast(const std::string &broadcastName) const override;
int findBroadcastById(const std::string &broadcastId) const override;

void addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock) override;
void addGreenFlagScript(std::shared_ptr<Block> hatBlock) override;
void addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, int fieldId, Broadcast *broadcast) override;
void addBackdropChangeScript(std::shared_ptr<Block> hatBlock, int fieldId) override;
Expand Down Expand Up @@ -172,6 +173,7 @@ class Engine : public IEngine
private:
enum class HatType
{
WhenTouchingObject,
GreenFlag,
BroadcastReceived,
BackdropChanged,
Expand Down Expand Up @@ -242,6 +244,7 @@ class Engine : public IEngine
std::recursive_mutex m_eventLoopMutex;
std::string m_userAgent;

std::unordered_map<Target *, std::vector<Script *>> m_whenTouchingObjectHats;
std::unordered_map<Target *, std::vector<Script *>> m_greenFlagHats;
std::unordered_map<Target *, std::vector<Script *>> m_backdropChangeHats;
std::unordered_map<Target *, std::vector<Script *>> m_broadcastHats;
Expand All @@ -252,7 +255,7 @@ class Engine : public IEngine

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

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
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

std::unique_ptr<ITimer> m_defaultTimer;
ITimer *m_timer = nullptr;
Expand Down
36 changes: 16 additions & 20 deletions src/engine/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,28 @@ void Script::setBytecode(const std::vector<unsigned int> &code)
void Script::setHatPredicateBytecode(const std::vector<unsigned int> &code)
{
impl->hatPredicateBytecodeVector = code;

if (impl->engine && !code.empty()) {
impl->hatPredicateVm = std::make_shared<VirtualMachine>(impl->engine->stage(), impl->engine, this);
impl->hatPredicateVm->setBytecode(impl->hatPredicateBytecodeVector.data());
impl->hatPredicateVm->setConstValues(impl->constValues);
}
}

/*!
* Runs the edge-activated hat predicate and returns the reported value.
* Runs the edge-activated hat predicate as the given target and returns the reported value.
* \note If there isn't any predicate, nothing will happen and the returned value will be false.
*/
bool Script::runHatPredicate()
bool Script::runHatPredicate(Target *target)
{
if (impl->hatPredicateVm && impl->hatPredicateVm->bytecode()) {
impl->hatPredicateVm->reset();
impl->hatPredicateVm->setFunctions(getFunctions());
impl->hatPredicateVm->run();
assert(impl->hatPredicateVm->registerCount() == 1);

if (impl->hatPredicateVm->registerCount() == 1)
return impl->hatPredicateVm->getInput(0, 1)->toBool();
}
if (!target || !impl->engine || impl->hatPredicateBytecodeVector.empty())
return false;

auto vm = std::make_shared<VirtualMachine>(target, impl->engine, this);
vm->setBytecode(impl->hatPredicateBytecodeVector.data());
vm->setConstValues(impl->constValues);
vm->setFunctions(getFunctions());
vm->setVariables(impl->variableValues.data());
vm->setLists(impl->lists.data());
vm->run();
assert(vm->registerCount() == 1);

if (vm->registerCount() == 1)
return vm->getInput(0, 1)->toBool();

return false;
}
Expand Down Expand Up @@ -161,9 +160,6 @@ void Script::setConstValues(const std::vector<Value> &values)
{
impl->constValuesVector = values;
impl->constValues = impl->constValuesVector.data();

if (impl->hatPredicateVm)
impl->hatPredicateVm->setConstValues(impl->constValues);
}

/*! Sets the list of variables. */
Expand Down
2 changes: 0 additions & 2 deletions src/engine/script_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ struct ScriptPrivate

unsigned int *bytecode = nullptr;
std::vector<unsigned int> bytecodeVector;

std::vector<unsigned int> hatPredicateBytecodeVector;
std::shared_ptr<VirtualMachine> hatPredicateVm;

Target *target = nullptr;
std::shared_ptr<Block> topBlock;
Expand Down
Loading