#if TESTS_BUILD and ANGELSCRIPT #include #include "../../TestLibrary/TestLibrary.hpp" #include "../../src/Battling/Pokemon/CreatePokemon.hpp" #include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp" #include "../../src/ScriptResolving/AngelScript/ContextPool.hpp" #define AS_CLASS(name, contents) \ { #name, "namespace Pokemon{ [Pokemon effect=" #name "] shared class " #name " : PkmnScript { " contents "}}" } static std::unordered_map _scripts = std::unordered_map{ AS_CLASS(blankScript, ), AS_CLASS(initializeScript, R"( uint64 length = 0; bool boolValue = false; int64 intValue = 0; constString stringValue; void OnInitialize(const BattleLibrary@ library, const narray@ parameters) override { length = parameters.Length; boolValue = parameters[0].AsBool(); intValue = parameters[1].AsInt(); stringValue = parameters[2].AsString(); } uint64 GetLength() { return length; } bool GetBoolValue() { return boolValue; } int64 GetIntValue() { return intValue; } constString GetStringValue() { return stringValue; } )"), AS_CLASS(stackScript, "int value = 0; void Stack() override { value++; } int GetValue() { return value; }"), AS_CLASS(onRemoveScript, "int value = 0; void OnRemove() override { value++; } int GetValue() { return value; }"), {"doubleInheritanceScript", R"( class doubleInheritanceScriptBase : PkmnScript { int value = 0; void Stack() override{ value++; } int GetValue(){ return value; } } [Pokemon effect=doubleInheritanceScript] class doubleInheritanceScript : doubleInheritanceScriptBase {} )"}, AS_CLASS(preventAttackScript, R"(void PreventAttack(ExecutingMove@ attack, bool& result) override{ result = !result; })"), AS_CLASS(stopBeforeAttackScript, R"( void StopBeforeAttack(ExecutingMove@ attack, bool& result) override{ result = !result; })"), AS_CLASS(OnBeforeAttackScript, "int value = 0; void OnBeforeAttack(ExecutingMove@ attack) override { value++; } " "int GetValue() { return value; }"), AS_CLASS( FailIncomingAttackScript, R"(void FailIncomingAttack(ExecutingMove@ attack, Pokemon@ target, bool& result) override{ result = !result; })"), AS_CLASS( IsInvulnerableScript, R"(void IsInvulnerable(ExecutingMove@ attack, Pokemon@ target, bool& result) override{ result = !result; })"), AS_CLASS(OnAttackMissScript, R"( int value = 0; void OnAttackMiss(ExecutingMove@ attack, Pokemon@ target) override { value++; } int GetValue() { return value; } )"), AS_CLASS( ChangeAttackTypeScript, R"(void ChangeAttackType(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint8& outType) override{outType = 1; };)"), AS_CLASS( ChangeEffectivenessScript, R"(void ChangeEffectiveness(ExecutingMove@ attack, Pokemon@ target, uint8 hit, float& eff) override{eff = 0.75; };)"), AS_CLASS( PreventSecondaryEffectsScript, R"(void PreventSecondaryEffects(ExecutingMove@ attack, Pokemon@ target, uint8 hit, bool& result) override{ result = !result; })"), AS_CLASS(OnSecondaryEffectScript, R"( int value = 0; void OnSecondaryEffect(ExecutingMove@ attack, Pokemon@ target, uint8 hit) override { value++; } int GetValue() { return value; })"), AS_CLASS(OnAfterHitsScript, "int value = 0; void OnAfterHits(ExecutingMove@ attack, Pokemon@ target) override { value++; } " "int GetValue() { return value; }"), AS_CLASS(throwScript, R"(void PreventAttack(ExecutingMove@ attack, bool& result) override{ throw("test exception"); })"), AS_CLASS(AddVolatileModMain, R"( void OnSecondaryEffect(ExecutingMove@ attack, Pokemon@ target, uint8 hit) override { auto script = cast(target.AddVolatile("AddVolatileModSecondary")); script.value++; }; )"), AS_CLASS(AddVolatileModSecondary, R"( int value = 0; int GetValue() { return value; } )"), }; static AngelScriptResolver* GetScriptResolver(PkmnLib::Battling::BattleLibrary* mainLib) { auto res = dynamic_cast(mainLib->GetScriptResolver().get()); res->Reset(mainLib); for (auto kv : _scripts) { res->CreateScript(kv.first, kv.second); } res->FinalizeModule(); return res; } static AngelScriptScript* GetScript(PkmnLib::Battling::BattleLibrary* mainLib, const ArbUt::StringView& scriptName) { auto lib = GetScriptResolver(mainLib); auto s = lib->LoadScript(nullptr, ScriptCategory::Creature, scriptName).TakeOwnership(); auto script = dynamic_cast(s); REQUIRE(script != nullptr); return script; } TEST_CASE("Invoke non-implemented script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "blankScript"_cnc); REQUIRE(script != nullptr); script->Stack(); delete script; } TEST_CASE("Invoke OnInitialize script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "initializeScript"_cnc); REQUIRE(script != nullptr); auto parameters = {new CreatureLib::Library::EffectParameter(true), new CreatureLib::Library::EffectParameter((int64_t)684), new CreatureLib::Library::EffectParameter(ArbUt::StringView("foobar"))}; script->OnInitialize(mainLib, parameters); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetLength"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE_EQ((uint64_t)ctx->GetReturnQWord(), 3); ctxPool->ReturnContextToPool(ctx); ctx = ctxPool->RequestContext(); script->PrepareMethod("GetBoolValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE((bool)ctx->GetReturnDWord()); ctxPool->ReturnContextToPool(ctx); ctx = ctxPool->RequestContext(); script->PrepareMethod("GetIntValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnQWord() == 684); ctxPool->ReturnContextToPool(ctx); ctx = ctxPool->RequestContext(); script->PrepareMethod("GetStringValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); ctxPool->ReturnContextToPool(ctx); for (auto p : parameters) { delete p; } delete script; } TEST_CASE("Invoke Stack script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "stackScript"_cnc); REQUIRE(script != nullptr); for (int i = 1; i <= 10; i++) { script->Stack(); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == i); ctxPool->ReturnContextToPool(ctx); } delete script; } TEST_CASE("Invoke OnRemove script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "onRemoveScript"_cnc); REQUIRE(script != nullptr); script->OnRemove(); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == 1); ctxPool->ReturnContextToPool(ctx); delete script; } TEST_CASE("Invoke Stack script function with implementation in base class") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "doubleInheritanceScript"_cnc); for (int i = 1; i <= 10; i++) { script->Stack(); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == i); ctxPool->ReturnContextToPool(ctx); } delete script; } TEST_CASE("Invoke preventAttackScript script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "preventAttackScript"_cnc); bool b = false; script->PreventAttack(nullptr, &b); REQUIRE(b); delete script; } TEST_CASE("Invoke StopBeforeAttack script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "stopBeforeAttackScript"_cnc); bool b = false; script->StopBeforeAttack(nullptr, &b); REQUIRE(b); delete script; } TEST_CASE("Invoke OnBeforeAttack script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "OnBeforeAttackScript"_cnc); REQUIRE(script != nullptr); script->OnBeforeAttack(nullptr); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == 1); ctxPool->ReturnContextToPool(ctx); delete script; } TEST_CASE("Invoke FailIncomingAttack script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "FailIncomingAttackScript"_cnc); REQUIRE(script != nullptr); bool b = false; script->FailIncomingAttack(nullptr, nullptr, &b); REQUIRE(b); delete script; } TEST_CASE("Invoke OnAttackMiss script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "OnAttackMissScript"_cnc); REQUIRE(script != nullptr); script->OnAttackMiss(nullptr, nullptr); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == 1); ctxPool->ReturnContextToPool(ctx); delete script; } TEST_CASE("Invoke ChangeAttackType script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "ChangeAttackTypeScript"_cnc); REQUIRE(script != nullptr); uint8_t b = 0; script->ChangeAttackType(nullptr, nullptr, 0, &b); REQUIRE(b == 1); delete script; } TEST_CASE("Invoke ChangeEffectiveness script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "ChangeEffectivenessScript"_cnc); REQUIRE(script != nullptr); float b = 0; script->ChangeEffectiveness(nullptr, nullptr, 0, &b); REQUIRE(b == doctest::Approx(0.75)); delete script; } TEST_CASE("Invoke PreventSecondaryEffects script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "PreventSecondaryEffectsScript"_cnc); bool b = false; script->PreventSecondaryEffects(nullptr, nullptr, 0, &b); REQUIRE(b); delete script; } TEST_CASE("Invoke OnSecondaryEffect script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "OnSecondaryEffectScript"_cnc); script->OnSecondaryEffect(nullptr, nullptr, 0); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == 1); ctxPool->ReturnContextToPool(ctx); delete script; } TEST_CASE("Invoke OnAfterHits script function") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "OnAfterHitsScript"_cnc); REQUIRE(script != nullptr); script->OnAfterHits(nullptr, nullptr); auto ctxPool = script->GetContextPool(); auto ctx = ctxPool->RequestContext(); script->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == 1); ctxPool->ReturnContextToPool(ctx); delete script; } void TryException(AngelScriptScript* script) { bool b = false; script->PreventAttack(nullptr, &b); } TEST_CASE("Get script name.") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "throwScript"_cnc); REQUIRE(script != nullptr); INFO(script->GetName().c_str()); INFO(script->GetName().std_str()); CHECK(strcmp(script->GetName().c_str(), "throwScript") == 0); delete script; } TEST_CASE("Handle script exceptions.") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "throwScript"_cnc); REQUIRE(script != nullptr); try { TryException(script); } catch (const ArbUt::Exception& e) { REQUIRE(strcmp(e.what(), "Script exception in script 'throwScript', line 1. Message: 'test exception'.") == 0); delete script; return; } throw ArbUt::Exception("Didn't throw"); } static PkmnLib::Library::TimeOfDay GetTime() { return PkmnLib::Library::TimeOfDay::Morning; } TEST_CASE("Add Volatile with return script function") { auto statCalc = new PkmnLib::Battling::StatCalculator(); auto resolver = dynamic_cast(PkmnLib::Battling::BattleLibrary::CreateScriptResolver()); auto mainLib = new PkmnLib::Battling::BattleLibrary( TestLibrary::BuildStaticLibrary(), statCalc, new PkmnLib::Battling::DamageLibrary(), new PkmnLib::Battling::ExperienceLibrary(), resolver, new PkmnLib::Battling::MiscLibrary(GetTime), new PkmnLib::Battling::CaptureLibrary()); resolver->Initialize(mainLib); for (auto kv : _scripts) { resolver->CreateScript(kv.first, kv.second); } resolver->FinalizeModule(); auto script = GetScript(mainLib, "AddVolatileModMain"_cnc); auto mon = PkmnLib::Battling::CreatePokemon(mainLib, "testSpecies"_cnc, 30).Build(); script->OnSecondaryEffect(nullptr, mon, 0); ArbUt::BorrowedPtr scriptObj = (CreatureLib::Battling::BattleScript*)1; REQUIRE(const_cast(mon->GetScriptIterator()).GetNext(scriptObj)); REQUIRE(scriptObj->GetName() == "AddVolatileModSecondary"_cnc); auto script2 = scriptObj.As(); auto ctxPool = script2->GetContextPool(); auto ctx = ctxPool->RequestContext(); script2->PrepareMethod("GetValue"_cnc, ctx); REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); REQUIRE(ctx->GetReturnDWord() == 1); ctxPool->ReturnContextToPool(ctx); delete mon; delete script; delete mainLib; } #endif