diff --git a/src/ScriptResolving/AngelScript/AngelScripResolver.cpp b/src/ScriptResolving/AngelScript/AngelScripResolver.cpp index 0b8d4d9..eaedf68 100644 --- a/src/ScriptResolving/AngelScript/AngelScripResolver.cpp +++ b/src/ScriptResolving/AngelScript/AngelScripResolver.cpp @@ -13,6 +13,7 @@ #include "TypeRegistry/Battling/RegisterPokemonClass.hpp" #include "TypeRegistry/Battling/RegisterTurnChoices.hpp" #include "TypeRegistry/ConstString.hpp" +#include "TypeRegistry/Library/RegisterEffectParameter.hpp" #include "TypeRegistry/Library/RegisterGrowthRateTypes.hpp" #include "TypeRegistry/Library/RegisterItemTypes.hpp" #include "TypeRegistry/Library/RegisterMoveTypes.hpp" @@ -24,6 +25,26 @@ CreatureLib::Battling::ScriptResolver* PkmnLib::Battling::BattleLibrary::CreateS return new AngelScripResolver(); } +static void TranslateException(asIScriptContext *ctx, void* /*userParam*/) +{ + try + { + // Retrow the original exception so we can catch it again + throw; + } + catch( std::exception &e ) + { + // Tell the VM the type of exception that occurred + ctx->SetException(e.what()); + } + catch(...) + { + // The callback must not allow any exception to be thrown, but it is not necessary + // to explicitly set an exception string if the default exception string is sufficient + } +} + + void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) { for (auto scriptCategory : ScriptCategoryHelper::GetValues()) { _typeDatabase.Insert(scriptCategory, {}); @@ -31,6 +52,7 @@ void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) { auto library = (PkmnLib::Battling::BattleLibrary*)arg; _engine = asCreateScriptEngine(); + _engine->SetTranslateAppExceptionCallback(asFUNCTION(TranslateException), 0, asCALL_CDECL); int32_t r = _engine->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL); if (r < 0) @@ -76,6 +98,7 @@ void AngelScripResolver::RegisterTypes() { RegisterGrowthRateTypes::Register(_engine); RegisterTypeLibrary::Register(_engine); RegisterStaticLibraryTypes::Register(_engine); + RegisterEffectParameter::Register(_engine); // Register battle types RegisterPokemonClass::Register(_engine); diff --git a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp index e26b019..5a46ed8 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptScript.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptScript.hpp @@ -4,6 +4,7 @@ #define ANGELSCRIPT_DLL_LIBRARY_IMPORT #include #include +#include "../../../extern/angelscript_addons/scriptarray/scriptarray.h" #include "../../Battling/PkmnScript.hpp" #include "AngelScriptTypeInfo.hpp" #include "ContextPool.hpp" @@ -73,6 +74,28 @@ public: ctx->PopState(); \ } + static CScriptArray* + GetEffectParameters(asIScriptContext* ctx, + const Arbutils::Collections::List& ls) { + asIScriptEngine* engine = ctx->GetEngine(); + asITypeInfo* t = engine->GetTypeInfoByDecl("array"); + CScriptArray* arr = CScriptArray::Create(t, ls.Count()); + for (size_t i = 0; i < ls.Count(); i++) { + arr->SetValue(i, (void**)&ls[i]); + } + return arr; + } + + void OnInitialize(const Arbutils::Collections::List& parameters) override { + CScriptArray* arr = nullptr; + CALL_HOOK(OnInitialize, { + arr = GetEffectParameters(ctx, parameters); + ctx->SetArgAddress(0, arr); + }) + if (arr != nullptr){ + arr->Release(); + } + } void Stack() override { CALL_HOOK(Stack, ); } void OnRemove() override { CALL_HOOK(OnRemove, ); } diff --git a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp index 0d33064..7a818f0 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp @@ -77,6 +77,7 @@ private: public: \ const FunctionInfo& Get##name() const { return __##name; } + SCRIPT_HOOK_FUNCTION(OnInitialize, "void OnInitialize(const array &in parameters)"); SCRIPT_HOOK_FUNCTION(Stack, "void Stack()"); SCRIPT_HOOK_FUNCTION(OnRemove, "void OnRemove()"); SCRIPT_HOOK_FUNCTION(PreventAttack, "void PreventAttack(ExecutingMove@ attack, bool& result)"); diff --git a/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp b/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp index d753b73..9832b61 100644 --- a/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp +++ b/src/ScriptResolving/AngelScript/TypeRegistry/BasicScriptClass.cpp @@ -6,7 +6,8 @@ void BasicScriptClass::Register(asIScriptEngine* engine) { // registry interface. As such, we just create it from string. [[maybe_unused]] int r = engine->GetModuleByIndex(0)->AddScriptSection("PkmnScript", R"( shared abstract class PkmnScript { -// CreatureLib methods + // CreatureLib methods + void OnInitialize(const array &in parameters){}; void Stack(){}; void OnRemove(){}; void PreventAttack(ExecutingMove@ attack, bool& result){}; @@ -33,10 +34,9 @@ shared abstract class PkmnScript { void ModifyDamageModifier(ExecutingMove@ attack, Pokemon@ target, uint8 hit, float& modifier){}; void OverrideDamage(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint& damage){}; -// PkmnLib methods + // PkmnLib methods void ModifyCriticalStage(ExecutingMove@ attack, Pokemon@ target, uint8 hit, uint8& critStage){}; } )"); assert(r >= 0); - } diff --git a/src/ScriptResolving/AngelScript/TypeRegistry/Library/RegisterEffectParameter.cpp b/src/ScriptResolving/AngelScript/TypeRegistry/Library/RegisterEffectParameter.cpp new file mode 100644 index 0000000..a5af72b --- /dev/null +++ b/src/ScriptResolving/AngelScript/TypeRegistry/Library/RegisterEffectParameter.cpp @@ -0,0 +1,38 @@ +#include "RegisterEffectParameter.hpp" +#include +#include + +static CreatureLib::Library::EffectParameter* Ref_Factory() { return new CreatureLib::Library::EffectParameter(); } + +static std::string AsString(const CreatureLib::Library::EffectParameter* p) { return p->AsString(); } + +void RegisterEffectParameter::Register(asIScriptEngine* engine) { + [[maybe_unused]] int r = engine->RegisterEnum("EffectParameterType"); + Assert(r >= 0); + for (auto val : CreatureLib::Library::EffectParameterTypeHelper::GetValues()) { + r = engine->RegisterEnumValue("EffectParameterType", + CreatureLib::Library::EffectParameterTypeHelper::ToString(val), (int)val); + Assert(r >= 0); + } + + r = engine->RegisterObjectType("EffectParameter", 0, asOBJ_REF | asOBJ_NOCOUNT); + Assert(r >= 0); + + r = engine->RegisterObjectBehaviour("EffectParameter", asBEHAVE_FACTORY, "EffectParameter@ f()", + asFUNCTION(Ref_Factory), asCALL_CDECL); + Assert(r >= 0); + r = engine->RegisterObjectMethod("EffectParameter", "EffectParameterType GetType() const", + asMETHOD(CreatureLib::Library::EffectParameter, GetType), asCALL_THISCALL); + Assert(r >= 0); + + r = engine->RegisterObjectMethod("EffectParameter", "bool AsBool() const", + asMETHOD(CreatureLib::Library::EffectParameter, AsBool), asCALL_THISCALL); + Assert(r >= 0); + r = engine->RegisterObjectMethod("EffectParameter", "int64 AsInt() const", + asMETHOD(CreatureLib::Library::EffectParameter, AsInt), asCALL_THISCALL); + Assert(r >= 0); + r = engine->RegisterObjectMethod( + "EffectParameter", "string AsString() const", + asFUNCTIONPR(AsString, (const CreatureLib::Library::EffectParameter*), std::string), asCALL_CDECL_OBJFIRST); + Assert(r >= 0); +} diff --git a/src/ScriptResolving/AngelScript/TypeRegistry/Library/RegisterEffectParameter.hpp b/src/ScriptResolving/AngelScript/TypeRegistry/Library/RegisterEffectParameter.hpp new file mode 100644 index 0000000..2d6bd50 --- /dev/null +++ b/src/ScriptResolving/AngelScript/TypeRegistry/Library/RegisterEffectParameter.hpp @@ -0,0 +1,10 @@ +#ifndef PKMNLIB_REGISTEREFFECTPARAMETER_HPP +#define PKMNLIB_REGISTEREFFECTPARAMETER_HPP + +#include +class RegisterEffectParameter { +public: + static void Register(asIScriptEngine* engine); +}; + +#endif // PKMNLIB_REGISTEREFFECTPARAMETER_HPP diff --git a/tests/ScriptTests/BaseScriptClassTests.cpp b/tests/ScriptTests/BaseScriptClassTests.cpp index 5e222ad..d3fba7d 100644 --- a/tests/ScriptTests/BaseScriptClassTests.cpp +++ b/tests/ScriptTests/BaseScriptClassTests.cpp @@ -9,6 +9,20 @@ static std::unordered_map _scripts = std::unordered_map{ AS_CLASS(blankScript, ), + AS_CLASS(initializeScript, R"( +bool boolValue = false; +int64 intValue = 0; +string stringValue = ""; +void OnInitialize(const array &in parameters) override { + boolValue = parameters[0].AsBool(); + intValue = parameters[1].AsInt(); + stringValue = parameters[2].AsString(); +} +bool GetBoolValue() { return boolValue; } +int64 GetIntValue() { return intValue; } +string 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"( @@ -93,6 +107,47 @@ TEST_CASE("Invoke non-implemented script function") { 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(std::string("foobar")) + }; + + script->OnInitialize(parameters); + + auto ctxPool = script->GetContextPool(); + auto 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); + std::string s; + s = *(std::string*)ctx->GetReturnAddress(); + REQUIRE(s == "foobar"); + 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); @@ -296,7 +351,6 @@ TEST_CASE("Get script name.") { delete script; } - TEST_CASE("Handle script exceptions.") { auto mainLib = TestLibrary::GetLibrary(); auto script = GetScript(mainLib, "throwScript"_cnc);