#ifndef PKMNLIB_ANGELSCRIPTSCRIPT_HPP #define PKMNLIB_ANGELSCRIPTSCRIPT_HPP #include #define ANGELSCRIPT_DLL_LIBRARY_IMPORT #include #include #include "../../../extern/angelscript_addons/scriptarray/scriptarray.h" #include "../../Battling/PkmnScript.hpp" #include "AngelScriptTypeInfo.hpp" #include "ContextPool.hpp" class AngelScriptScript : public PkmnLib::Battling::PkmnScript { private: AngelScriptTypeInfo* _type = nullptr; ContextPool* _ctxPool = nullptr; asIScriptObject* _obj = nullptr; public: AngelScriptScript(AngelScriptTypeInfo* type, asIScriptObject* obj, ContextPool* ctxPool) : _type(type), _ctxPool(ctxPool), _obj(obj) {} ~AngelScriptScript() override { _obj->Release(); } [[nodiscard]] const ConstString& GetName() const noexcept override { return _type->GetName(); } asIScriptFunction* PrepareMethod(const ConstString& name, asIScriptContext* ctx) { auto func = _type->GetFunction(name); ctx->Prepare(func); ctx->SetObject(_obj); return func; } ContextPool* GetContextPool() { return _ctxPool; } #define CALL_HOOK(name, setup) \ auto s = _type->Get##name(); \ if (!s.Exists) \ return; \ auto ctx = asGetActiveContext(); \ bool newContext = false; \ if (ctx == nullptr) { \ ctx = _ctxPool->RequestContext(); \ newContext = true; \ } else { \ ctx->PushState(); \ } \ ctx->Prepare(s.Function); \ ctx->SetObject(_obj); \ setup; \ auto scriptResult = ctx->Execute(); \ if (scriptResult != asEXECUTION_FINISHED) { \ if (scriptResult == asEXECUTION_EXCEPTION) { \ std::stringstream err; \ err << "Script exception in script '" << GetName().c_str() << "', line " << ctx->GetExceptionLineNumber() \ << ". Message: '" << ctx->GetExceptionString() << "'."; \ if (newContext) { \ _ctxPool->ReturnContextToPool(ctx); \ } else { \ ctx->PopState(); \ } \ throw CreatureException(err.str()); \ } \ if (newContext) { \ _ctxPool->ReturnContextToPool(ctx); \ } else { \ ctx->PopState(); \ } \ throw CreatureException("Script didn't finish properly; message " + std::to_string(scriptResult)); \ } \ if (newContext) { \ _ctxPool->ReturnContextToPool(ctx); \ } else { \ 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, ); } void OnBeforeTurn(const CreatureLib::Battling::BaseTurnChoice* choice) override { throw NotImplementedException(); // TODO } void ChangeAttack(CreatureLib::Battling::AttackTurnChoice* choice, ConstString* outAttack) override { throw NotImplementedException(); // TODO } void PreventAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outResult) override { CALL_HOOK(PreventAttack, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgAddress(1, outResult); }) } void FailAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outFailed) override { CALL_HOOK(FailAttack, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgAddress(1, outFailed); }) } void StopBeforeAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outResult) override { CALL_HOOK(StopBeforeAttack, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgAddress(1, outResult); }) } void OnBeforeAttack(CreatureLib::Battling::ExecutingAttack* attack) override { CALL_HOOK(OnBeforeAttack, { ctx->SetArgObject(0, (void*)attack); }) } void FailIncomingAttack(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, bool* outResult) override { CALL_HOOK(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 { CALL_HOOK(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 { CALL_HOOK(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 { CALL_HOOK(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 { CALL_HOOK(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 { CALL_HOOK(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 { CALL_HOOK(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 { CALL_HOOK(OnAfterHits, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); }) } void PreventSelfSwitch(const CreatureLib::Battling::SwitchTurnChoice* choice, bool* outResult) override { throw NotImplementedException(); // TODO } void ModifyEffectChance(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, float* chance) override { CALL_HOOK(ModifyEffectChance, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgAddress(2, chance); }) } void ModifyIncomingEffectChance(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, float* chance) override { CALL_HOOK(ModifyIncomingEffectChance, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgAddress(2, chance); }) } void OverrideBasePower(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, uint8_t* basePower) override { CALL_HOOK(OverrideBasePower, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, basePower); }) } void ChangeDamageStatsUser(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, CreatureLib::Battling::Creature** statsUser) override { CALL_HOOK(ChangeDamageStatsUser, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, statsUser); }) } void BypassDefensiveStat(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, bool* bypass) override { CALL_HOOK(BypassDefensiveStat, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, bypass); }) } void BypassOffensiveStat(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, bool* bypass) override { CALL_HOOK(BypassOffensiveStat, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, bypass); }) } void ModifyStatModifier(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, float* modifier) override { CALL_HOOK(ModifyStatModifier, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, modifier); }) } void ModifyDamageModifier(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, float* modifier) override { CALL_HOOK(ModifyDamageModifier, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, modifier); }) } void OverrideDamage(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hitIndex, uint32_t* damage) override { CALL_HOOK(OverrideDamage, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hitIndex); ctx->SetArgAddress(3, damage); }) } //////////////////// // PkmnLib methods// //////////////////// void ModifyCriticalStage(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target, uint8_t hit, uint8_t* critStage) override { CALL_HOOK(ModifyCriticalStage, { ctx->SetArgObject(0, (void*)attack); ctx->SetArgObject(1, (void*)target); ctx->SetArgByte(2, hit); ctx->SetArgAddress(3, critStage); }) } }; #undef CALL_HOOK #endif // PKMNLIB_ANGELSCRIPTSCRIPT_HPP