Skip to content

Commit 4902c4e

Browse files
authored
Merge pull request #191 from scratchcpp/clone_blocks
Implement clone blocks
2 parents 1405c37 + d5ed24a commit 4902c4e

File tree

7 files changed

+326
-5
lines changed

7 files changed

+326
-5
lines changed

include/scratchcpp/target.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class LIBSCRATCHCPP_EXPORT Target
2424
public:
2525
Target();
2626
Target(const Target &) = delete;
27+
virtual ~Target() { }
2728

2829
/*! Returns true if this Target is the stage. */
2930
virtual bool isStage() const { return false; }

src/blocks/controlblocks.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
#include <scratchcpp/iengine.h>
44
#include <scratchcpp/compiler.h>
5+
#include <scratchcpp/sprite.h>
6+
#include <scratchcpp/input.h>
57
#include <scratchcpp/field.h>
8+
#include <scratchcpp/block.h>
69
#include <cassert>
710

811
#include "controlblocks.h"
@@ -27,6 +30,9 @@ void ControlBlocks::registerBlocks(IEngine *engine)
2730
engine->addCompileFunction(this, "control_stop", &compileStop);
2831
engine->addCompileFunction(this, "control_wait", &compileWait);
2932
engine->addCompileFunction(this, "control_wait_until", &compileWaitUntil);
33+
engine->addCompileFunction(this, "control_start_as_clone", &compileStartAsClone);
34+
engine->addCompileFunction(this, "control_create_clone_of", &compileCreateClone);
35+
engine->addCompileFunction(this, "control_delete_this_clone", &compileDeleteThisClone);
3036

3137
// Inputs
3238
engine->addInput(this, "SUBSTACK", SUBSTACK);
@@ -35,6 +41,7 @@ void ControlBlocks::registerBlocks(IEngine *engine)
3541
engine->addInput(this, "CONDITION", CONDITION);
3642
engine->addInput(this, "DURATION", DURATION);
3743
engine->addInput(this, "VALUE", VALUE);
44+
engine->addInput(this, "CLONE_OPTION", CLONE_OPTION);
3845

3946
// Fields
4047
engine->addField(this, "STOP_OPTION", STOP_OPTION);
@@ -155,6 +162,37 @@ void ControlBlocks::compileWaitUntil(Compiler *compiler)
155162
compiler->addFunctionCall(&waitUntil);
156163
}
157164

165+
void ControlBlocks::compileStartAsClone(Compiler *compiler)
166+
{
167+
compiler->engine()->addCloneInitScript(compiler->block());
168+
}
169+
170+
void ControlBlocks::compileCreateClone(Compiler *compiler)
171+
{
172+
Input *input = compiler->input(CLONE_OPTION);
173+
174+
if (input->type() != Input::Type::ObscuredShadow) {
175+
assert(input->pointsToDropdownMenu());
176+
std::string spriteName = input->selectedMenuItem();
177+
178+
if (spriteName == "_myself_")
179+
compiler->addFunctionCall(&createCloneOfMyself);
180+
else {
181+
int index = compiler->engine()->findTarget(spriteName);
182+
compiler->addConstValue(index);
183+
compiler->addFunctionCall(&createCloneByIndex);
184+
}
185+
} else {
186+
compiler->addInput(input);
187+
compiler->addFunctionCall(&createClone);
188+
}
189+
}
190+
191+
void ControlBlocks::compileDeleteThisClone(Compiler *compiler)
192+
{
193+
compiler->addFunctionCall(&deleteThisClone);
194+
}
195+
158196
unsigned int ControlBlocks::stopAll(VirtualMachine *vm)
159197
{
160198
vm->engine()->stop();
@@ -196,3 +234,54 @@ unsigned int ControlBlocks::waitUntil(VirtualMachine *vm)
196234
}
197235
return 1;
198236
}
237+
238+
unsigned int ControlBlocks::createClone(VirtualMachine *vm)
239+
{
240+
std::string spriteName = vm->getInput(0, 1)->toString();
241+
Target *target;
242+
243+
if (spriteName == "_myself_")
244+
target = vm->target();
245+
else
246+
target = vm->engine()->targetAt(vm->engine()->findTarget(spriteName));
247+
248+
Sprite *sprite = dynamic_cast<Sprite *>(target);
249+
250+
if (sprite)
251+
sprite->clone();
252+
253+
return 1;
254+
}
255+
256+
unsigned int ControlBlocks::createCloneByIndex(VirtualMachine *vm)
257+
{
258+
Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt());
259+
Sprite *sprite = dynamic_cast<Sprite *>(target);
260+
261+
if (sprite)
262+
sprite->clone();
263+
264+
return 1;
265+
}
266+
267+
unsigned int ControlBlocks::createCloneOfMyself(VirtualMachine *vm)
268+
{
269+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
270+
271+
if (sprite)
272+
sprite->clone();
273+
274+
return 0;
275+
}
276+
277+
unsigned int ControlBlocks::deleteThisClone(VirtualMachine *vm)
278+
{
279+
Target *target = vm->target();
280+
281+
if (target) {
282+
vm->engine()->stopTarget(target, nullptr);
283+
target->~Target();
284+
}
285+
286+
return 0;
287+
}

src/blocks/controlblocks.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class ControlBlocks : public IBlockSection
2323
TIMES,
2424
CONDITION,
2525
DURATION,
26-
VALUE
26+
VALUE,
27+
CLONE_OPTION
2728
};
2829

2930
enum Fields
@@ -53,12 +54,19 @@ class ControlBlocks : public IBlockSection
5354
static void compileStop(Compiler *compiler);
5455
static void compileWait(Compiler *compiler);
5556
static void compileWaitUntil(Compiler *compiler);
57+
static void compileStartAsClone(Compiler *compiler);
58+
static void compileCreateClone(Compiler *compiler);
59+
static void compileDeleteThisClone(Compiler *compiler);
5660

5761
static unsigned int stopAll(VirtualMachine *vm);
5862
static unsigned int stopOtherScriptsInSprite(VirtualMachine *vm);
5963
static unsigned int startWait(VirtualMachine *vm);
6064
static unsigned int wait(VirtualMachine *vm);
6165
static unsigned int waitUntil(VirtualMachine *vm);
66+
static unsigned int createClone(VirtualMachine *vm);
67+
static unsigned int createCloneByIndex(VirtualMachine *vm);
68+
static unsigned int createCloneOfMyself(VirtualMachine *vm);
69+
static unsigned int deleteThisClone(VirtualMachine *vm);
6270

6371
static inline std::unordered_map<VirtualMachine *, std::pair<std::chrono::steady_clock::time_point, int>> m_timeMap;
6472
};

src/engine/internal/engine.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ void Engine::frame()
143143
}
144144
}
145145

146-
m_scriptsToRemove.push_back(script.get());
146+
if (std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), script.get()) == m_scriptsToRemove.end())
147+
m_scriptsToRemove.push_back(script.get());
147148
}
148149
} while (!script->atEnd() && !m_breakFrame);
149150
}
@@ -262,7 +263,8 @@ void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wa
262263
void Engine::stopScript(VirtualMachine *vm)
263264
{
264265
assert(vm);
265-
m_scriptsToRemove.push_back(vm);
266+
if (std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm) == m_scriptsToRemove.end())
267+
m_scriptsToRemove.push_back(vm);
266268
}
267269

268270
void Engine::stopTarget(Target *target, VirtualMachine *exceptScript)
@@ -298,7 +300,7 @@ void Engine::initClone(Sprite *clone)
298300
#ifndef NDEBUG
299301
// Since we're initializing the clone, it shouldn't have any running scripts
300302
for (const auto script : m_runningScripts)
301-
assert(script->target() != clone);
303+
assert((script->target() != clone) || (std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), script.get()) != m_scriptsToRemove.end()));
302304
#endif
303305

304306
for (auto script : scripts) {

0 commit comments

Comments
 (0)