From 27a732f682324a3011f6ad00316a7a65a37724d1 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:15:54 +0100 Subject: [PATCH 1/4] Implement looks_say block --- src/blocks/looksblocks.cpp | 20 +++++++++++++ src/blocks/looksblocks.h | 3 ++ test/blocks/looks_blocks_test.cpp | 48 +++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index 4d553499..c47a9867 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -25,6 +25,7 @@ std::string LooksBlocks::name() const void LooksBlocks::registerBlocks(IEngine *engine) { // Blocks + engine->addCompileFunction(this, "looks_say", &compileSay); engine->addCompileFunction(this, "looks_show", &compileShow); engine->addCompileFunction(this, "looks_hide", &compileHide); engine->addCompileFunction(this, "looks_changeeffectby", &compileChangeEffectBy); @@ -49,6 +50,7 @@ void LooksBlocks::registerBlocks(IEngine *engine) engine->addMonitorNameFunction(this, "looks_size", &sizeMonitorName); // Inputs + engine->addInput(this, "MESSAGE", MESSAGE); engine->addInput(this, "CHANGE", CHANGE); engine->addInput(this, "SIZE", SIZE); engine->addInput(this, "COSTUME", COSTUME); @@ -77,6 +79,12 @@ void LooksBlocks::registerBlocks(IEngine *engine) engine->addFieldValue(this, "backward", Backward); } +void LooksBlocks::compileSay(Compiler *compiler) +{ + compiler->addInput(MESSAGE); + compiler->addFunctionCall(&say); +} + void LooksBlocks::compileShow(Compiler *compiler) { compiler->addFunctionCall(&show); @@ -515,6 +523,18 @@ const std::string &LooksBlocks::sizeMonitorName(Block *block) return name; } +unsigned int LooksBlocks::say(VirtualMachine *vm) +{ + Target *target = vm->target(); + + if (target) { + target->setBubbleType(Target::BubbleType::Say); + target->setBubbleText(vm->getInput(0, 1)->toString()); + } + + return 1; +} + unsigned int LooksBlocks::show(VirtualMachine *vm) { Sprite *sprite = dynamic_cast(vm->target()); diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index 585fc0ec..2f1faf0b 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -20,6 +20,7 @@ class LooksBlocks : public IBlockSection public: enum Inputs { + MESSAGE, CHANGE, SIZE, COSTUME, @@ -57,6 +58,7 @@ class LooksBlocks : public IBlockSection void registerBlocks(IEngine *engine) override; + static void compileSay(Compiler *compiler); static void compileShow(Compiler *compiler); static void compileHide(Compiler *compiler); static void compileChangeEffectBy(Compiler *compiler); @@ -79,6 +81,7 @@ class LooksBlocks : public IBlockSection static const std::string &backdropNumberNameMonitorName(Block *block); static const std::string &sizeMonitorName(Block *block); + static unsigned int say(VirtualMachine *vm); static unsigned int show(VirtualMachine *vm); static unsigned int hide(VirtualMachine *vm); diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp index 9057051a..a3f7552d 100644 --- a/test/blocks/looks_blocks_test.cpp +++ b/test/blocks/looks_blocks_test.cpp @@ -104,6 +104,7 @@ TEST_F(LooksBlocksTest, CategoryVisible) TEST_F(LooksBlocksTest, RegisterBlocks) { // Blocks + EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_say", &LooksBlocks::compileSay)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_show", &LooksBlocks::compileShow)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_hide", &LooksBlocks::compileHide)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_changeeffectby", &LooksBlocks::compileChangeEffectBy)); @@ -128,6 +129,7 @@ TEST_F(LooksBlocksTest, RegisterBlocks) EXPECT_CALL(m_engineMock, addMonitorNameFunction(m_section.get(), "looks_size", &LooksBlocks::sizeMonitorName)); // Inputs + EXPECT_CALL(m_engineMock, addInput(m_section.get(), "MESSAGE", LooksBlocks::MESSAGE)); EXPECT_CALL(m_engineMock, addInput(m_section.get(), "CHANGE", LooksBlocks::CHANGE)); EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SIZE", LooksBlocks::SIZE)); EXPECT_CALL(m_engineMock, addInput(m_section.get(), "COSTUME", LooksBlocks::COSTUME)); @@ -158,6 +160,52 @@ TEST_F(LooksBlocksTest, RegisterBlocks) m_section->registerBlocks(&m_engineMock); } +TEST_F(LooksBlocksTest, Say) +{ + Compiler compiler(&m_engineMock); + + // say "Hello!" + auto block = std::make_shared("a", "looks_say"); + addValueInput(block, "MESSAGE", LooksBlocks::MESSAGE, "Hello!"); + + EXPECT_CALL(m_engineMock, functionIndex(&LooksBlocks::say)).WillOnce(Return(0)); + + compiler.init(); + compiler.setBlock(block); + LooksBlocks::compileSay(&compiler); + compiler.end(); + + ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT })); + ASSERT_EQ(compiler.constValues().size(), 1); + ASSERT_EQ(compiler.constValues()[0].toString(), "Hello!"); +} + +TEST_F(LooksBlocksTest, SayImpl) +{ + static unsigned int bytecode[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; + static BlockFunc functions[] = { &LooksBlocks::say }; + static Value constValues[] = { "test" }; + + Target target; + VirtualMachine vm(&target, nullptr, nullptr); + vm.setBytecode(bytecode); + vm.setFunctions(functions); + vm.setConstValues(constValues); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); + + target.setBubbleType(Target::BubbleType::Think); + vm.reset(); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); +} + TEST_F(LooksBlocksTest, Show) { Compiler compiler(&m_engineMock); From dfdaf52e8d4ad8cff6b76c5da39f4d9966a920b9 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 31 Jan 2024 19:40:25 +0100 Subject: [PATCH 2/4] Implement looks_sayforsecs block --- src/blocks/looksblocks.cpp | 90 +++++++++++++++- src/blocks/looksblocks.h | 19 +++- test/blocks/looks_blocks_test.cpp | 164 ++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 4 deletions(-) diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index c47a9867..8a22eb17 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -12,10 +12,12 @@ #include "looksblocks.h" #include "../engine/internal/randomgenerator.h" +#include "../engine/internal/clock.h" using namespace libscratchcpp; IRandomGenerator *LooksBlocks::rng = nullptr; +IClock *LooksBlocks::clock = nullptr; std::string LooksBlocks::name() const { @@ -25,6 +27,7 @@ std::string LooksBlocks::name() const void LooksBlocks::registerBlocks(IEngine *engine) { // Blocks + engine->addCompileFunction(this, "looks_sayforsecs", &compileSayForSecs); engine->addCompileFunction(this, "looks_say", &compileSay); engine->addCompileFunction(this, "looks_show", &compileShow); engine->addCompileFunction(this, "looks_hide", &compileHide); @@ -51,6 +54,7 @@ void LooksBlocks::registerBlocks(IEngine *engine) // Inputs engine->addInput(this, "MESSAGE", MESSAGE); + engine->addInput(this, "SECS", SECS); engine->addInput(this, "CHANGE", CHANGE); engine->addInput(this, "SIZE", SIZE); engine->addInput(this, "COSTUME", COSTUME); @@ -79,6 +83,14 @@ void LooksBlocks::registerBlocks(IEngine *engine) engine->addFieldValue(this, "backward", Backward); } +void LooksBlocks::compileSayForSecs(Compiler *compiler) +{ + compiler->addInput(MESSAGE); + compiler->addInput(SECS); + compiler->addFunctionCall(&startSayForSecs); + compiler->addFunctionCall(&sayForSecs); +} + void LooksBlocks::compileSay(Compiler *compiler) { compiler->addInput(MESSAGE); @@ -523,15 +535,87 @@ const std::string &LooksBlocks::sizeMonitorName(Block *block) return name; } -unsigned int LooksBlocks::say(VirtualMachine *vm) +void LooksBlocks::startWait(VirtualMachine *vm, double secs) +{ + if (!clock) + clock = Clock::instance().get(); + + auto currentTime = clock->currentSteadyTime(); + m_timeMap[vm] = { currentTime, secs * 1000 }; + vm->engine()->requestRedraw(); +} + +bool LooksBlocks::wait(VirtualMachine *vm) +{ + if (!clock) + clock = Clock::instance().get(); + + auto currentTime = clock->currentSteadyTime(); + assert(m_timeMap.count(vm) == 1); + + if (std::chrono::duration_cast(currentTime - m_timeMap[vm].first).count() >= m_timeMap[vm].second) { + m_timeMap.erase(vm); + vm->stop(true, true, false); + return true; + } else { + vm->stop(true, true, true); + return false; + } +} + +void LooksBlocks::showBubble(VirtualMachine *vm, Target::BubbleType type, const std::string &text) { Target *target = vm->target(); if (target) { - target->setBubbleType(Target::BubbleType::Say); - target->setBubbleText(vm->getInput(0, 1)->toString()); + target->setBubbleType(type); + target->setBubbleText(text); + m_waitingBubbles.erase(target); } +} +void LooksBlocks::hideBubble(Target *target) +{ + if (!target) + return; + + target->setBubbleText(""); + m_waitingBubbles.erase(target); +} + +unsigned int LooksBlocks::startSayForSecs(VirtualMachine *vm) +{ + Target *target = vm->target(); + + if (target) { + showBubble(vm, Target::BubbleType::Say, vm->getInput(0, 2)->toString()); + m_waitingBubbles[target] = vm; + startWait(vm, vm->getInput(1, 2)->toDouble()); + } + + return 2; +} + +unsigned int LooksBlocks::sayForSecs(VirtualMachine *vm) +{ + if (wait(vm)) { + Target *target = vm->target(); + + if (target) { + auto it = m_waitingBubbles.find(target); + + // Clear bubble if it hasn't been changed + if (it != m_waitingBubbles.cend() && it->second == vm) + hideBubble(vm->target()); + } + } + + return 0; +} + +unsigned int LooksBlocks::say(VirtualMachine *vm) +{ + showBubble(vm, Target::BubbleType::Say, vm->getInput(0, 1)->toString()); return 1; } diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index 2f1faf0b..0c6df4ff 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -3,16 +3,19 @@ #pragma once #include +#include #include +#include +#include namespace libscratchcpp { -class Target; class Stage; class Value; class IGraphicsEffect; class IRandomGenerator; +class IClock; /*! \brief The LooksBlocks class contains the implementation of looks blocks. */ class LooksBlocks : public IBlockSection @@ -21,6 +24,7 @@ class LooksBlocks : public IBlockSection enum Inputs { MESSAGE, + SECS, CHANGE, SIZE, COSTUME, @@ -58,6 +62,7 @@ class LooksBlocks : public IBlockSection void registerBlocks(IEngine *engine) override; + static void compileSayForSecs(Compiler *compiler); static void compileSay(Compiler *compiler); static void compileShow(Compiler *compiler); static void compileHide(Compiler *compiler); @@ -81,7 +86,15 @@ class LooksBlocks : public IBlockSection static const std::string &backdropNumberNameMonitorName(Block *block); static const std::string &sizeMonitorName(Block *block); + static void startWait(VirtualMachine *vm, double secs); + static bool wait(VirtualMachine *vm); + static void showBubble(VirtualMachine *vm, Target::BubbleType type, const std::string &text); + static void hideBubble(Target *target); + + static unsigned int startSayForSecs(VirtualMachine *vm); + static unsigned int sayForSecs(VirtualMachine *vm); static unsigned int say(VirtualMachine *vm); + static unsigned int show(VirtualMachine *vm); static unsigned int hide(VirtualMachine *vm); @@ -144,6 +157,9 @@ class LooksBlocks : public IBlockSection static unsigned int backdropNumber(VirtualMachine *vm); static unsigned int backdropName(VirtualMachine *vm); + static inline std::unordered_map> m_timeMap; + static inline std::unordered_map m_waitingBubbles; + static inline std::vector m_customGraphicsEffects; static inline IGraphicsEffect *m_colorEffect = nullptr; static inline IGraphicsEffect *m_fisheyeEffect = nullptr; @@ -154,6 +170,7 @@ class LooksBlocks : public IBlockSection static inline IGraphicsEffect *m_ghostEffect = nullptr; static IRandomGenerator *rng; + static IClock *clock; }; } // namespace libscratchcpp diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp index a3f7552d..2b8dd403 100644 --- a/test/blocks/looks_blocks_test.cpp +++ b/test/blocks/looks_blocks_test.cpp @@ -9,12 +9,14 @@ #include #include #include +#include #include "../common.h" #include "blocks/looksblocks.h" #include "blocks/operatorblocks.h" #include "engine/internal/engine.h" #include "engine/internal/randomgenerator.h" +#include "engine/internal/clock.h" using namespace libscratchcpp; @@ -104,6 +106,7 @@ TEST_F(LooksBlocksTest, CategoryVisible) TEST_F(LooksBlocksTest, RegisterBlocks) { // Blocks + EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_sayforsecs", &LooksBlocks::compileSayForSecs)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_say", &LooksBlocks::compileSay)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_show", &LooksBlocks::compileShow)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_hide", &LooksBlocks::compileHide)); @@ -130,6 +133,7 @@ TEST_F(LooksBlocksTest, RegisterBlocks) // Inputs EXPECT_CALL(m_engineMock, addInput(m_section.get(), "MESSAGE", LooksBlocks::MESSAGE)); + EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SECS", LooksBlocks::SECS)); EXPECT_CALL(m_engineMock, addInput(m_section.get(), "CHANGE", LooksBlocks::CHANGE)); EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SIZE", LooksBlocks::SIZE)); EXPECT_CALL(m_engineMock, addInput(m_section.get(), "COSTUME", LooksBlocks::COSTUME)); @@ -160,6 +164,166 @@ TEST_F(LooksBlocksTest, RegisterBlocks) m_section->registerBlocks(&m_engineMock); } +TEST_F(LooksBlocksTest, SayForSecs) +{ + Compiler compiler(&m_engineMock); + + // say "Hello!" for 3.5 seconds + auto block = std::make_shared("a", "looks_sayforsecs"); + addValueInput(block, "MESSAGE", LooksBlocks::MESSAGE, "Hello!"); + addValueInput(block, "SECS", LooksBlocks::SECS, 3.5); + + EXPECT_CALL(m_engineMock, functionIndex(&LooksBlocks::startSayForSecs)).WillOnce(Return(0)); + EXPECT_CALL(m_engineMock, functionIndex(&LooksBlocks::sayForSecs)).WillOnce(Return(1)); + + compiler.init(); + compiler.setBlock(block); + LooksBlocks::compileSayForSecs(&compiler); + compiler.end(); + + ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT })); + ASSERT_EQ(compiler.constValues(), std::vector({ "Hello!", 3.5 })); +} + +TEST_F(LooksBlocksTest, SayForSecsImpl) +{ + static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }; + static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_HALT }; + static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }; + static BlockFunc functions[] = { &LooksBlocks::startSayForSecs, &LooksBlocks::sayForSecs, &LooksBlocks::say }; + static Value constValues[] = { "test", 5.5, "hello" }; + + Target target; + target.setBubbleType(Target::BubbleType::Think); + VirtualMachine vm(&target, &m_engineMock, nullptr); + vm.setFunctions(functions); + vm.setConstValues(constValues); + vm.setBytecode(bytecode1); + + ClockMock clock; + LooksBlocks::clock = &clock; + + std::chrono::steady_clock::time_point startTime(std::chrono::milliseconds(1000)); + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); + + std::chrono::steady_clock::time_point time1(std::chrono::milliseconds(6450)); + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time1)); + target.setBubbleType(Target::BubbleType::Think); + target.setBubbleText("another"); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "another"); + + std::chrono::steady_clock::time_point time2(std::chrono::milliseconds(6500)); + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2)); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_TRUE(target.bubbleText().empty()); + + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_TRUE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_TRUE(target.bubbleText().empty()); + + // Run the say block while waiting + VirtualMachine vm2(&target, &m_engineMock, nullptr); + vm2.setFunctions(functions); + vm2.setConstValues(constValues); + vm2.setBytecode(bytecode2); + + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm.reset(); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); + + vm2.run(); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "hello"); + + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2)); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "hello"); + + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_TRUE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "hello"); + + // Run the say for secs block while waiting + vm2.reset(); + vm2.setBytecode(bytecode3); + + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm2.reset(); + vm2.run(); + + ASSERT_EQ(vm2.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm2.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "hello"); + + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm.reset(); + vm.run(); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); + + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2)); + vm2.run(); + + ASSERT_EQ(vm2.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) == LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm2.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); + + vm2.run(); + + ASSERT_EQ(vm2.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) == LooksBlocks::m_timeMap.cend()); + ASSERT_TRUE(vm2.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "test"); + + LooksBlocks::clock = Clock::instance().get(); +} + TEST_F(LooksBlocksTest, Say) { Compiler compiler(&m_engineMock); From 2000df59f3a9b89572c821978b552b0b1f52e8d3 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 31 Jan 2024 19:50:37 +0100 Subject: [PATCH 3/4] Implement looks_thinkforsecs block --- src/blocks/looksblocks.cpp | 27 +++++ src/blocks/looksblocks.h | 4 + test/blocks/looks_blocks_test.cpp | 161 ++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index 8a22eb17..34eea64d 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -29,6 +29,7 @@ void LooksBlocks::registerBlocks(IEngine *engine) // Blocks engine->addCompileFunction(this, "looks_sayforsecs", &compileSayForSecs); engine->addCompileFunction(this, "looks_say", &compileSay); + engine->addCompileFunction(this, "looks_thinkforsecs", &compileThinkForSecs); engine->addCompileFunction(this, "looks_show", &compileShow); engine->addCompileFunction(this, "looks_hide", &compileHide); engine->addCompileFunction(this, "looks_changeeffectby", &compileChangeEffectBy); @@ -97,6 +98,14 @@ void LooksBlocks::compileSay(Compiler *compiler) compiler->addFunctionCall(&say); } +void LooksBlocks::compileThinkForSecs(Compiler *compiler) +{ + compiler->addInput(MESSAGE); + compiler->addInput(SECS); + compiler->addFunctionCall(&startThinkForSecs); + compiler->addFunctionCall(&thinkForSecs); +} + void LooksBlocks::compileShow(Compiler *compiler) { compiler->addFunctionCall(&show); @@ -619,6 +628,24 @@ unsigned int LooksBlocks::say(VirtualMachine *vm) return 1; } +unsigned int LooksBlocks::startThinkForSecs(VirtualMachine *vm) +{ + Target *target = vm->target(); + + if (target) { + showBubble(vm, Target::BubbleType::Think, vm->getInput(0, 2)->toString()); + m_waitingBubbles[target] = vm; + startWait(vm, vm->getInput(1, 2)->toDouble()); + } + + return 2; +} + +unsigned int LooksBlocks::thinkForSecs(VirtualMachine *vm) +{ + return sayForSecs(vm); // there isn't any difference +} + unsigned int LooksBlocks::show(VirtualMachine *vm) { Sprite *sprite = dynamic_cast(vm->target()); diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index 0c6df4ff..ac61a173 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -64,6 +64,7 @@ class LooksBlocks : public IBlockSection static void compileSayForSecs(Compiler *compiler); static void compileSay(Compiler *compiler); + static void compileThinkForSecs(Compiler *compiler); static void compileShow(Compiler *compiler); static void compileHide(Compiler *compiler); static void compileChangeEffectBy(Compiler *compiler); @@ -95,6 +96,9 @@ class LooksBlocks : public IBlockSection static unsigned int sayForSecs(VirtualMachine *vm); static unsigned int say(VirtualMachine *vm); + static unsigned int startThinkForSecs(VirtualMachine *vm); + static unsigned int thinkForSecs(VirtualMachine *vm); + static unsigned int show(VirtualMachine *vm); static unsigned int hide(VirtualMachine *vm); diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp index 2b8dd403..3ccb8a0a 100644 --- a/test/blocks/looks_blocks_test.cpp +++ b/test/blocks/looks_blocks_test.cpp @@ -108,6 +108,7 @@ TEST_F(LooksBlocksTest, RegisterBlocks) // Blocks EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_sayforsecs", &LooksBlocks::compileSayForSecs)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_say", &LooksBlocks::compileSay)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_thinkforsecs", &LooksBlocks::compileThinkForSecs)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_show", &LooksBlocks::compileShow)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_hide", &LooksBlocks::compileHide)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_changeeffectby", &LooksBlocks::compileChangeEffectBy)); @@ -370,6 +371,166 @@ TEST_F(LooksBlocksTest, SayImpl) ASSERT_EQ(target.bubbleText(), "test"); } +TEST_F(LooksBlocksTest, ThinkForSecs) +{ + Compiler compiler(&m_engineMock); + + // think "Hmm..." for 3.5 seconds + auto block = std::make_shared("a", "looks_thinkforsecs"); + addValueInput(block, "MESSAGE", LooksBlocks::MESSAGE, "Hmm..."); + addValueInput(block, "SECS", LooksBlocks::SECS, 3.5); + + EXPECT_CALL(m_engineMock, functionIndex(&LooksBlocks::startThinkForSecs)).WillOnce(Return(0)); + EXPECT_CALL(m_engineMock, functionIndex(&LooksBlocks::thinkForSecs)).WillOnce(Return(1)); + + compiler.init(); + compiler.setBlock(block); + LooksBlocks::compileThinkForSecs(&compiler); + compiler.end(); + + ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT })); + ASSERT_EQ(compiler.constValues(), std::vector({ "Hmm...", 3.5 })); +} + +TEST_F(LooksBlocksTest, ThinkForSecsImpl) +{ + static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }; + // static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_HALT }; + static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }; + static BlockFunc functions[] = { &LooksBlocks::startThinkForSecs, &LooksBlocks::thinkForSecs /*, &LooksBlocks::think*/ }; + static Value constValues[] = { "test", 5.5, "hello" }; + + Target target; + target.setBubbleType(Target::BubbleType::Say); + VirtualMachine vm(&target, &m_engineMock, nullptr); + vm.setFunctions(functions); + vm.setConstValues(constValues); + vm.setBytecode(bytecode1); + + ClockMock clock; + LooksBlocks::clock = &clock; + + std::chrono::steady_clock::time_point startTime(std::chrono::milliseconds(1000)); + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test"); + + std::chrono::steady_clock::time_point time1(std::chrono::milliseconds(6450)); + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time1)); + target.setBubbleType(Target::BubbleType::Say); + target.setBubbleText("another"); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_EQ(target.bubbleText(), "another"); + + std::chrono::steady_clock::time_point time2(std::chrono::milliseconds(6500)); + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2)); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_TRUE(target.bubbleText().empty()); + + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_TRUE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Say); + ASSERT_TRUE(target.bubbleText().empty()); + + // Run the say block while waiting + /*VirtualMachine vm2(&target, &m_engineMock, nullptr); + vm2.setFunctions(functions); + vm2.setConstValues(constValues); + vm2.setBytecode(bytecode2); + + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm.reset(); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test"); + + vm2.run(); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "hello"); + + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2)); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "hello"); + + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm) == LooksBlocks::m_timeMap.cend()); + ASSERT_TRUE(vm.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "hello"); + + // Run the say for secs block while waiting + vm2.reset(); + vm2.setBytecode(bytecode3); + + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm2.reset(); + vm2.run(); + + ASSERT_EQ(vm2.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) != LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm2.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "hello"); + + EXPECT_CALL(clock, currentSteadyTime()).Times(2).WillRepeatedly(Return(startTime)); + EXPECT_CALL(m_engineMock, requestRedraw()); + vm.reset(); + vm.run(); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test"); + + EXPECT_CALL(clock, currentSteadyTime()).WillOnce(Return(time2)); + vm2.run(); + + ASSERT_EQ(vm2.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) == LooksBlocks::m_timeMap.cend()); + ASSERT_FALSE(vm2.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test"); + + vm2.run(); + + ASSERT_EQ(vm2.registerCount(), 0); + ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) == LooksBlocks::m_timeMap.cend()); + ASSERT_TRUE(vm2.atEnd()); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test");*/ + + LooksBlocks::clock = Clock::instance().get(); +} + TEST_F(LooksBlocksTest, Show) { Compiler compiler(&m_engineMock); From 30d945b32fdfe7d2245fe0111aaa74f4a8f097ec Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 31 Jan 2024 19:57:07 +0100 Subject: [PATCH 4/4] Implement looks_think block --- src/blocks/looksblocks.cpp | 13 ++++++++ src/blocks/looksblocks.h | 2 ++ test/blocks/looks_blocks_test.cpp | 55 ++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index 34eea64d..0072ed45 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -30,6 +30,7 @@ void LooksBlocks::registerBlocks(IEngine *engine) engine->addCompileFunction(this, "looks_sayforsecs", &compileSayForSecs); engine->addCompileFunction(this, "looks_say", &compileSay); engine->addCompileFunction(this, "looks_thinkforsecs", &compileThinkForSecs); + engine->addCompileFunction(this, "looks_think", &compileThink); engine->addCompileFunction(this, "looks_show", &compileShow); engine->addCompileFunction(this, "looks_hide", &compileHide); engine->addCompileFunction(this, "looks_changeeffectby", &compileChangeEffectBy); @@ -106,6 +107,12 @@ void LooksBlocks::compileThinkForSecs(Compiler *compiler) compiler->addFunctionCall(&thinkForSecs); } +void LooksBlocks::compileThink(Compiler *compiler) +{ + compiler->addInput(MESSAGE); + compiler->addFunctionCall(&think); +} + void LooksBlocks::compileShow(Compiler *compiler) { compiler->addFunctionCall(&show); @@ -646,6 +653,12 @@ unsigned int LooksBlocks::thinkForSecs(VirtualMachine *vm) return sayForSecs(vm); // there isn't any difference } +unsigned int LooksBlocks::think(VirtualMachine *vm) +{ + showBubble(vm, Target::BubbleType::Think, vm->getInput(0, 1)->toString()); + return 1; +} + unsigned int LooksBlocks::show(VirtualMachine *vm) { Sprite *sprite = dynamic_cast(vm->target()); diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index ac61a173..91a598b4 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -65,6 +65,7 @@ class LooksBlocks : public IBlockSection static void compileSayForSecs(Compiler *compiler); static void compileSay(Compiler *compiler); static void compileThinkForSecs(Compiler *compiler); + static void compileThink(Compiler *compiler); static void compileShow(Compiler *compiler); static void compileHide(Compiler *compiler); static void compileChangeEffectBy(Compiler *compiler); @@ -98,6 +99,7 @@ class LooksBlocks : public IBlockSection static unsigned int startThinkForSecs(VirtualMachine *vm); static unsigned int thinkForSecs(VirtualMachine *vm); + static unsigned int think(VirtualMachine *vm); static unsigned int show(VirtualMachine *vm); static unsigned int hide(VirtualMachine *vm); diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp index 3ccb8a0a..288c4781 100644 --- a/test/blocks/looks_blocks_test.cpp +++ b/test/blocks/looks_blocks_test.cpp @@ -109,6 +109,7 @@ TEST_F(LooksBlocksTest, RegisterBlocks) EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_sayforsecs", &LooksBlocks::compileSayForSecs)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_say", &LooksBlocks::compileSay)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_thinkforsecs", &LooksBlocks::compileThinkForSecs)); + EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_think", &LooksBlocks::compileThink)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_show", &LooksBlocks::compileShow)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_hide", &LooksBlocks::compileHide)); EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "looks_changeeffectby", &LooksBlocks::compileChangeEffectBy)); @@ -395,9 +396,9 @@ TEST_F(LooksBlocksTest, ThinkForSecs) TEST_F(LooksBlocksTest, ThinkForSecsImpl) { static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }; - // static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_HALT }; + static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_HALT }; static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }; - static BlockFunc functions[] = { &LooksBlocks::startThinkForSecs, &LooksBlocks::thinkForSecs /*, &LooksBlocks::think*/ }; + static BlockFunc functions[] = { &LooksBlocks::startThinkForSecs, &LooksBlocks::thinkForSecs, &LooksBlocks::think }; static Value constValues[] = { "test", 5.5, "hello" }; Target target; @@ -452,7 +453,7 @@ TEST_F(LooksBlocksTest, ThinkForSecsImpl) ASSERT_TRUE(target.bubbleText().empty()); // Run the say block while waiting - /*VirtualMachine vm2(&target, &m_engineMock, nullptr); + VirtualMachine vm2(&target, &m_engineMock, nullptr); vm2.setFunctions(functions); vm2.setConstValues(constValues); vm2.setBytecode(bytecode2); @@ -526,11 +527,57 @@ TEST_F(LooksBlocksTest, ThinkForSecsImpl) ASSERT_TRUE(LooksBlocks::m_timeMap.find(&vm2) == LooksBlocks::m_timeMap.cend()); ASSERT_TRUE(vm2.atEnd()); ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); - ASSERT_EQ(target.bubbleText(), "test");*/ + ASSERT_EQ(target.bubbleText(), "test"); LooksBlocks::clock = Clock::instance().get(); } +TEST_F(LooksBlocksTest, Think) +{ + Compiler compiler(&m_engineMock); + + // say "Hmm..." + auto block = std::make_shared("a", "looks_think"); + addValueInput(block, "MESSAGE", LooksBlocks::MESSAGE, "Hmm..."); + + EXPECT_CALL(m_engineMock, functionIndex(&LooksBlocks::think)).WillOnce(Return(0)); + + compiler.init(); + compiler.setBlock(block); + LooksBlocks::compileThink(&compiler); + compiler.end(); + + ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT })); + ASSERT_EQ(compiler.constValues().size(), 1); + ASSERT_EQ(compiler.constValues()[0].toString(), "Hmm..."); +} + +TEST_F(LooksBlocksTest, ThinkImpl) +{ + static unsigned int bytecode[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; + static BlockFunc functions[] = { &LooksBlocks::think }; + static Value constValues[] = { "test" }; + + Target target; + VirtualMachine vm(&target, nullptr, nullptr); + vm.setBytecode(bytecode); + vm.setFunctions(functions); + vm.setConstValues(constValues); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test"); + + target.setBubbleType(Target::BubbleType::Say); + vm.reset(); + vm.run(); + + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(target.bubbleType(), Target::BubbleType::Think); + ASSERT_EQ(target.bubbleText(), "test"); +} + TEST_F(LooksBlocksTest, Show) { Compiler compiler(&m_engineMock);