From b425a7e8b9003fb4a9687c24608c1cee2365fd81 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Wed, 5 Feb 2020 17:06:15 +0100 Subject: [PATCH] Implements most script hooks --- .../AngelScript/AngelScriptScript.hpp | 67 ++++++-- .../AngelScript/AngelScriptTypeInfo.hpp | 36 ++-- .../TypeRegistry/BasicScriptClass.cpp | 12 ++ tests/ScriptTests/BaseScriptClassTests.cpp | 156 ++++++++++++++++-- 4 files changed, 238 insertions(+), 33 deletions(-) diff --git a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp index f26c95f..5b02a40 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp @@ -2,8 +2,8 @@ #define PKMNLIB_ANGELSCRIPTSCRIPT_HPP #include #define ANGELSCRIPT_DLL_LIBRARY_IMPORT -#include #include +#include #include "AngelScriptTypeInfo.hpp" #include "ContextPool.hpp" @@ -46,11 +46,11 @@ public: void Stack() override { CALLHOOK(Stack, {}); } void OnBeforeTurn(const CreatureLib::Battling::BaseTurnChoice* choice) override { - throw NotImplementedException(); //TODO + throw NotImplementedException(); // TODO } void ChangeAttack(CreatureLib::Battling::AttackTurnChoice* choice, std::string* outAttack) override { - throw NotImplementedException(); //TODO + throw NotImplementedException(); // TODO } void PreventAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outResult) override { @@ -74,41 +74,82 @@ public: }) } - void OnBeforeAttack(CreatureLib::Battling::ExecutingAttack* attack) override { Script::OnBeforeAttack(attack); } + void OnBeforeAttack(CreatureLib::Battling::ExecutingAttack* attack) override { + CALLHOOK(OnBeforeAttack, { ctx->SetArgObject(0, (void*)attack); }) + } + void FailIncomingAttack(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, bool* outResult) override { - Script::FailIncomingAttack(attack, target, outResult); + CALLHOOK(FailIncomingAttack, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + ctx->SetArgAddress(2, outResult); + }) } void IsInvulnerable(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, bool* outResult) override { - Script::IsInvulnerable(attack, target, outResult); + CALLHOOK(IsInvulnerable, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + ctx->SetArgAddress(2, outResult); + }) } void OnAttackMiss(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target) override { - Script::OnAttackMiss(attack, target); + CALLHOOK(OnAttackMiss, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + }) } + void ChangeAttackType(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitNumber, uint8_t* outType) override { - Script::ChangeAttackType(attack, target, hitNumber, outType); + CALLHOOK(ChangeAttackType, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + ctx->SetArgByte(2, hitNumber); + ctx->SetArgAddress(3, outType); + }) } + void OnStatusMove(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitNumber) override { - Script::OnStatusMove(attack, target, hitNumber); + CALLHOOK(OnStatusMove, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + ctx->SetArgByte(2, hitNumber); + }) } + void PreventSecondaryEffects(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitNumber, bool* outResult) override { - Script::PreventSecondaryEffects(attack, target, hitNumber, outResult); + CALLHOOK(PreventSecondaryEffects, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + ctx->SetArgByte(2, hitNumber); + ctx->SetArgAddress(3, outResult); + }) } + void OnSecondaryEffect(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitNumber) override { - Script::OnSecondaryEffect(attack, target, hitNumber); + CALLHOOK(OnSecondaryEffect, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + ctx->SetArgByte(2, hitNumber); + }) } + void OnAfterHits(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target) override { - Script::OnAfterHits(attack, target); + CALLHOOK(OnAfterHits, { + ctx->SetArgObject(0, (void*)attack); + ctx->SetArgObject(1, (void*)target); + }) } + void PreventSelfSwitch(const CreatureLib::Battling::SwitchTurnChoice* choice, bool* outResult) override { - Script::PreventSelfSwitch(choice, outResult); + throw NotImplementedException(); // TODO } }; diff --git a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp index 0aaf3e4..2b6cfb0 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp @@ -19,10 +19,10 @@ private: FunctionInfo Initialize(const std::string& decl) { auto val = _type->GetMethodByDecl(decl.c_str(), false); - if (val == nullptr){ + if (val == nullptr) { return FunctionInfo{.Exists = false, .Function = nullptr}; } - if (!val->IsOverride()){ + if (!val->IsOverride()) { return FunctionInfo{.Exists = false, .Function = nullptr}; } return FunctionInfo{.Exists = true, .Function = val}; @@ -62,15 +62,31 @@ public: return obj; } -#define SCRIPT_HOOK_FUNCTION(name, decl) \ -private: FunctionInfo __##name = Initialize(decl); \ -public: const FunctionInfo& Get##name() const { return __##name; } - -SCRIPT_HOOK_FUNCTION(Stack, "void Stack()"); -SCRIPT_HOOK_FUNCTION(PreventAttack, "void PreventAttack(ExecutingMove@ attack, bool& result)"); -SCRIPT_HOOK_FUNCTION(FailAttack, "void FailAttack(ExecutingMove@ attack, bool& result)"); -SCRIPT_HOOK_FUNCTION(StopBeforeAttack, "void StopBeforeAttack(ExecutingMove@ attack, bool& result)"); +#define SCRIPT_HOOK_FUNCTION(name, decl) \ +private: \ + FunctionInfo __##name = Initialize(decl); \ + \ +public: \ + const FunctionInfo& Get##name() const { return __##name; } + SCRIPT_HOOK_FUNCTION(Stack, "void Stack()"); + SCRIPT_HOOK_FUNCTION(PreventAttack, "void PreventAttack(ExecutingMove@ attack, bool& result)"); + SCRIPT_HOOK_FUNCTION(FailAttack, "void FailAttack(ExecutingMove@ attack, bool& result)"); + SCRIPT_HOOK_FUNCTION(StopBeforeAttack, "void StopBeforeAttack(ExecutingMove@ attack, bool& result)"); + SCRIPT_HOOK_FUNCTION(OnBeforeAttack, "void OnBeforeAttack(ExecutingMove@ attack)"); + SCRIPT_HOOK_FUNCTION(FailIncomingAttack, + "void FailIncomingAttack(ExecutingMove@ attack, Pokemon@ target, bool& result)"); + SCRIPT_HOOK_FUNCTION(IsInvulnerable, "void IsInvulnerable(ExecutingMove@ attack, Pokemon@ target, bool& result)"); + SCRIPT_HOOK_FUNCTION(OnAttackMiss, "void OnAttackMiss(ExecutingMove@ attack, Pokemon@ target)"); + SCRIPT_HOOK_FUNCTION(ChangeAttackType, + "void ChangeAttackType(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint8& outType)"); + SCRIPT_HOOK_FUNCTION(OnStatusMove, "void OnStatusMove(ExecutingMove@ attack, Pokemon@ target, uint8 hit)"); + SCRIPT_HOOK_FUNCTION( + PreventSecondaryEffects, + "void PreventSecondaryEffects(ExecutingMove@ attack, Pokemon@ target, uint8 hit, bool& outResult)"); + SCRIPT_HOOK_FUNCTION(OnSecondaryEffect, + "void OnSecondaryEffect(ExecutingMove@ attack, Pokemon@ target, uint8 hit)"); + SCRIPT_HOOK_FUNCTION(OnAfterHits, "void OnAfterHits(ExecutingMove@ attack, Pokemon@ target)"); }; #undef SCRIPT_HOOK_FUNCTION diff --git a/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp b/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp index 2150e19..dfeef47 100644 --- a/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp +++ b/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp @@ -2,12 +2,24 @@ #include void BasicScriptClass::Register(asIScriptEngine* engine) { + // As far as I am aware at the moment you can't create an abstract class with virtual members through the application + // registry interface. As such, we just create it from string. [[maybe_unused]] int r = engine->GetModuleByIndex(0)->AddScriptSection("PkmnScript", R"( shared abstract class PkmnScript { void Stack(){}; void PreventAttack(ExecutingMove@ attack, bool& result){}; void FailAttack(ExecutingMove@ attack, bool& result){}; void StopBeforeAttack(ExecutingMove@ attack, bool& result){}; + void OnBeforeAttack(ExecutingMove@ attack){}; + void FailIncomingAttack(ExecutingMove@ attack, Pokemon@ target, bool& result){}; + void IsInvulnerable(ExecutingMove@ attack, Pokemon@ target, bool& result){}; + void OnAttackMiss(ExecutingMove@ attack, Pokemon@ target){}; + void ChangeAttackType(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint8& outType){}; + void OnStatusMove(ExecutingMove@ attack, Pokemon@ target, uint8 hit){}; + void PreventSecondaryEffects(ExecutingMove@ attack, Pokemon@ target, uint8 hit, bool& outResult){}; + void OnSecondaryEffect(ExecutingMove@ attack, Pokemon@ target, uint8 hit){}; + void OnAfterHits(ExecutingMove@ attack, Pokemon@ target){}; + } )"); assert(r >= 0); diff --git a/tests/ScriptTests/BaseScriptClassTests.cpp b/tests/ScriptTests/BaseScriptClassTests.cpp index 184f61e..ec2a652 100644 --- a/tests/ScriptTests/BaseScriptClassTests.cpp +++ b/tests/ScriptTests/BaseScriptClassTests.cpp @@ -4,12 +4,13 @@ #include "../../src/ScriptResolving/AngelScript/AngelScripResolver.hpp" #include "../TestLibrary/TestLibrary.hpp" -#define AS_CLASS(name, contents) { #name, "class " #name " : PkmnScript { " contents "}" } +#define AS_CLASS(name, contents) \ + { #name, "class " #name " : PkmnScript { " contents "}" } -static std::unordered_map _scripts = std::unordered_map { - AS_CLASS(blankScript, ), - AS_CLASS( stackScript, "int value = 0; void Stack() override { value++; } int GetValue() { return value; }"), - {"doubleInheritanceScript", R"( +static std::unordered_map _scripts = std::unordered_map{ + AS_CLASS(blankScript, ), + AS_CLASS(stackScript, "int value = 0; void Stack() override { value++; } int GetValue() { return value; }"), + {"doubleInheritanceScript", R"( class doubleInheritanceScriptBase : PkmnScript { int value = 0; void Stack() override{ @@ -20,14 +21,40 @@ class doubleInheritanceScriptBase : PkmnScript { } class doubleInheritanceScript : doubleInheritanceScriptBase {} )"}, - AS_CLASS( preventAttackScript, R"( -void PreventAttack(ExecutingMove@ attack, bool& result) override{ - result = !result; -})"), - AS_CLASS( stopBeforeAttackScript, R"( + 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, + "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( + OnStatusMoveScript, + "int value = 0; void OnStatusMove(ExecutingMove@ attack, Pokemon@ target, uint8 hit) override { value++; } " + "int GetValue() { return value; }"), + AS_CLASS( + PreventSecondaryEffectsScript, + R"(void PreventSecondaryEffects(ExecutingMove@ attack, Pokemon@ target, uint8 hit, bool& result) override{ result = !result; })"), + AS_CLASS(OnSecondaryEffectScript, "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; }"), }; @@ -115,5 +142,114 @@ TEST_CASE("Invoke StopBeforeAttack script function") { delete script; } +TEST_CASE("Invoke OnBeforeAttack script function") { + auto mainLib = TestLibrary::GetLibrary(); + auto script = GetScript(mainLib, "OnBeforeAttackScript"); + + script->OnBeforeAttack(nullptr); + + auto ctxPool = script->GetContextPool(); + auto ctx = ctxPool->RequestContext(); + script->PrepareMethod("GetValue", 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"); + 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"); + + script->OnAttackMiss(nullptr, nullptr); + + auto ctxPool = script->GetContextPool(); + auto ctx = ctxPool->RequestContext(); + script->PrepareMethod("GetValue", 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"); + uint8_t b = 0; + script->ChangeAttackType(nullptr, nullptr, 0, &b); + REQUIRE(b == 1); + + delete script; +} + +TEST_CASE("Invoke OnStatusMove script function") { + auto mainLib = TestLibrary::GetLibrary(); + auto script = GetScript(mainLib, "OnStatusMoveScript"); + + script->OnStatusMove(nullptr, nullptr, 0); + + auto ctxPool = script->GetContextPool(); + auto ctx = ctxPool->RequestContext(); + script->PrepareMethod("GetValue", ctx); + REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); + REQUIRE(ctx->GetReturnDWord() == 1); + ctxPool->ReturnContextToPool(ctx); + + delete script; +} + +TEST_CASE("Invoke PreventSecondaryEffects script function") { + auto mainLib = TestLibrary::GetLibrary(); + auto script = GetScript(mainLib, "PreventSecondaryEffectsScript"); + 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"); + + script->OnSecondaryEffect(nullptr, nullptr, 0); + + auto ctxPool = script->GetContextPool(); + auto ctx = ctxPool->RequestContext(); + script->PrepareMethod("GetValue", 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"); + + script->OnAfterHits(nullptr, nullptr); + + auto ctxPool = script->GetContextPool(); + auto ctx = ctxPool->RequestContext(); + script->PrepareMethod("GetValue", ctx); + REQUIRE(ctx->Execute() == asEXECUTION_FINISHED); + REQUIRE(ctx->GetReturnDWord() == 1); + ctxPool->ReturnContextToPool(ctx); + + delete script; +} #endif \ No newline at end of file