Initial support for item use scripts in angelscript.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
18cebe842a
commit
27dd8a8202
|
@ -5,13 +5,20 @@ using namespace PkmnLib::Library;
|
|||
|
||||
export Item* PkmnLib_Item_Construct(const char* name, CreatureLib::Library::ItemCategory category,
|
||||
CreatureLib::Library::BattleItemCategory battleCategory, int32_t price,
|
||||
const char* flags[], size_t flagsCount, uint8_t flingPower) {
|
||||
const char* effectName, CreatureLib::Library::EffectParameter* effectParameters[],
|
||||
size_t effectParameterCount, const char* flags[], size_t flagsCount,
|
||||
uint8_t flingPower) {
|
||||
std::unordered_set<uint32_t> conversedFlags(flagsCount);
|
||||
for (size_t i = 0; i < flagsCount; i++) {
|
||||
conversedFlags.insert(ArbUt::StringView::CalculateHash(flags[i]));
|
||||
}
|
||||
|
||||
return new Item(ArbUt::StringView(name), category, battleCategory, price, conversedFlags, flingPower);
|
||||
return new Item(
|
||||
ArbUt::StringView(name), category, battleCategory, price,
|
||||
new CreatureLib::Library::SecondaryEffect(100, effectName,
|
||||
ArbUt::List<CreatureLib::Library::EffectParameter*>(
|
||||
effectParameters, effectParameters + effectParameterCount)),
|
||||
conversedFlags, flingPower);
|
||||
};
|
||||
|
||||
export void PkmnLib_Item_Destruct(const Item* p) { delete p; }
|
||||
|
|
|
@ -9,8 +9,10 @@ namespace PkmnLib::Library {
|
|||
public:
|
||||
Item(const ArbUt::StringView& name, CreatureLib::Library::ItemCategory category,
|
||||
CreatureLib::Library::BattleItemCategory battleCategory, int32_t price,
|
||||
const std::unordered_set<uint32_t>& flags, uint8_t flingPower) noexcept
|
||||
: CreatureLib::Library::Item(name, category, battleCategory, price, flags), _flingPower(flingPower) {}
|
||||
const CreatureLib::Library::SecondaryEffect* effect, const std::unordered_set<uint32_t>& flags,
|
||||
uint8_t flingPower) noexcept
|
||||
: CreatureLib::Library::Item(name, category, battleCategory, price, effect, flags),
|
||||
_flingPower(flingPower) {}
|
||||
|
||||
inline uint8_t GetFlingPower() const noexcept { return _flingPower; }
|
||||
};
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef PKMNLIB_ANGELSCRIPTFUNCTIONCALL_HPP
|
||||
#define PKMNLIB_ANGELSCRIPTFUNCTIONCALL_HPP
|
||||
|
||||
#include "ContextPool.hpp"
|
||||
|
||||
class AngelScriptUtils {
|
||||
public:
|
||||
static void AngelscriptFunctionCall(asIScriptFunction* func, ContextPool* ctxPool, asIScriptObject* obj,
|
||||
const ArbUt::StringView& scriptName,
|
||||
const std::function<void(asIScriptContext*)>& setup,
|
||||
const std::function<void(asIScriptContext*)>& onEnd) {
|
||||
auto ctx = asGetActiveContext();
|
||||
bool newContext = false;
|
||||
if (ctx == nullptr) {
|
||||
ctx = ctxPool->RequestContext();
|
||||
newContext = true;
|
||||
} else {
|
||||
ctx->PushState();
|
||||
}
|
||||
ctx->Prepare(func);
|
||||
ctx->SetObject(obj);
|
||||
setup(ctx);
|
||||
auto scriptResult = ctx->Execute();
|
||||
if (scriptResult != asEXECUTION_FINISHED) {
|
||||
if (scriptResult == asEXECUTION_EXCEPTION) {
|
||||
std::stringstream err;
|
||||
err << "Script exception in script '" << scriptName.c_str() << "', line "
|
||||
<< ctx->GetExceptionLineNumber() << ". Message: '" << ctx->GetExceptionString() << "'.";
|
||||
if (newContext) {
|
||||
ctxPool->ReturnContextToPool(ctx);
|
||||
} else {
|
||||
ctx->PopState();
|
||||
}
|
||||
throw ArbUt::Exception(err.str());
|
||||
}
|
||||
if (newContext) {
|
||||
ctxPool->ReturnContextToPool(ctx);
|
||||
} else {
|
||||
ctx->PopState();
|
||||
}
|
||||
THROW("Script didn't finish properly; message " << scriptResult);
|
||||
}
|
||||
onEnd(ctx);
|
||||
if (newContext) {
|
||||
ctxPool->ReturnContextToPool(ctx);
|
||||
} else {
|
||||
ctx->PopState();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,65 @@
|
|||
#include "AngelScriptItemUseScript.hpp"
|
||||
#include "AngelScriptResolver.hpp"
|
||||
|
||||
bool AngelScriptItemUseScript::IsItemUsable() const {
|
||||
if (!__IsItemUsable.Exists) {
|
||||
return CreatureLib::Battling::ItemUseScript::IsItemUsable();
|
||||
}
|
||||
bool res = false;
|
||||
AngelScriptUtils::AngelscriptFunctionCall(
|
||||
__IsItemUsable.Function, _resolver->GetContextPool(), _scriptObject, ""_cnc,
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) {},
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) { res = ctx->GetReturnByte() == 1; });
|
||||
return res;
|
||||
}
|
||||
|
||||
bool AngelScriptItemUseScript::IsCreatureUseItem() const {
|
||||
if (!__IsPokemonUseItem.Exists) {
|
||||
return CreatureLib::Battling::ItemUseScript::IsCreatureUseItem();
|
||||
}
|
||||
bool res = false;
|
||||
AngelScriptUtils::AngelscriptFunctionCall(
|
||||
__IsPokemonUseItem.Function, _resolver->GetContextPool(), _scriptObject, ""_cnc,
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) {},
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) { res = ctx->GetReturnByte() == 1; });
|
||||
return res;
|
||||
}
|
||||
bool AngelScriptItemUseScript::IsUseValidForCreature(CreatureLib::Battling::Creature* creature) const {
|
||||
if (!__IsPokemonUseItem.Exists) {
|
||||
return CreatureLib::Battling::ItemUseScript::IsUseValidForCreature(creature);
|
||||
}
|
||||
bool res = false;
|
||||
AngelScriptUtils::AngelscriptFunctionCall(
|
||||
__IsPokemonUseItem.Function, _resolver->GetContextPool(), _scriptObject, ""_cnc,
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) { ctx->SetArgObject(0, (void*)creature); },
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) { res = ctx->GetReturnByte() == 1; });
|
||||
return res;
|
||||
}
|
||||
bool AngelScriptItemUseScript::IsHoldable() const {
|
||||
if (!__IsHoldable.Exists) {
|
||||
return CreatureLib::Battling::ItemUseScript::IsHoldable();
|
||||
}
|
||||
bool res = false;
|
||||
AngelScriptUtils::AngelscriptFunctionCall(
|
||||
__IsHoldable.Function, _resolver->GetContextPool(), _scriptObject, ""_cnc,
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) {},
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) { res = ctx->GetReturnByte() == 1; });
|
||||
return res;
|
||||
}
|
||||
void AngelScriptItemUseScript::OnUse() const {
|
||||
if (!__OnUse.Exists) {
|
||||
CreatureLib::Battling::ItemUseScript::OnUse();
|
||||
}
|
||||
AngelScriptUtils::AngelscriptFunctionCall(
|
||||
__OnUse.Function, _resolver->GetContextPool(), _scriptObject, ""_cnc,
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) {}, [&]([[maybe_unused]] asIScriptContext* ctx) {});
|
||||
}
|
||||
void AngelScriptItemUseScript::OnCreatureUse(CreatureLib::Battling::Creature* creature) const {
|
||||
if (!__OnPokemonUse.Exists) {
|
||||
CreatureLib::Battling::ItemUseScript::OnUse();
|
||||
}
|
||||
AngelScriptUtils::AngelscriptFunctionCall(
|
||||
__OnPokemonUse.Function, _resolver->GetContextPool(), _scriptObject, ""_cnc,
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) { ctx->SetArgObject(0, (void*)creature); },
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) {});
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef PKMNLIB_ANGELSCRIPTITEMUSESCRIPT_HPP
|
||||
#define PKMNLIB_ANGELSCRIPTITEMUSESCRIPT_HPP
|
||||
|
||||
#include <CreatureLib/Battling/ScriptHandling/ItemUseScript.hpp>
|
||||
#include "AngelScriptFunctionCall.hpp"
|
||||
|
||||
class AngelScriptResolver;
|
||||
|
||||
class AngelScriptItemUseScript final : public CreatureLib::Battling::ItemUseScript {
|
||||
public:
|
||||
AngelScriptItemUseScript(asIScriptObject* scriptObject, const AngelScriptResolver* resolver)
|
||||
: _scriptObject(scriptObject), _resolver(resolver) {}
|
||||
|
||||
[[nodiscard]] bool IsItemUsable() const override;
|
||||
[[nodiscard]] bool IsCreatureUseItem() const override;
|
||||
bool IsUseValidForCreature(CreatureLib::Battling::Creature* creature) const override;
|
||||
|
||||
bool IsHoldable() const override;
|
||||
|
||||
void OnUse() const override;
|
||||
void OnCreatureUse(CreatureLib::Battling::Creature* creature) const override;
|
||||
|
||||
private:
|
||||
asIScriptObject* _scriptObject;
|
||||
const AngelScriptResolver* _resolver;
|
||||
|
||||
struct FunctionInfo {
|
||||
bool Exists = false;
|
||||
asIScriptFunction* Function = nullptr;
|
||||
};
|
||||
|
||||
FunctionInfo Initialize(const std::string& decl) {
|
||||
auto val = _scriptObject->GetObjectType()->GetMethodByDecl(decl.c_str(), false);
|
||||
if (val == nullptr) {
|
||||
return FunctionInfo{.Exists = false, .Function = nullptr};
|
||||
}
|
||||
return FunctionInfo{.Exists = true, .Function = val};
|
||||
}
|
||||
|
||||
#define ITEM_USE_SCRIPT_HOOK_FUNCTION(name, decl) FunctionInfo __##name = Initialize(decl);
|
||||
|
||||
ITEM_USE_SCRIPT_HOOK_FUNCTION(IsItemUsable, "bool IsItemUsable()");
|
||||
ITEM_USE_SCRIPT_HOOK_FUNCTION(IsPokemonUseItem, "bool IsPokemonUseItem()");
|
||||
ITEM_USE_SCRIPT_HOOK_FUNCTION(IsUseValidForPokemon, "bool IsUseValidForPokemon(Pokemon@ target)");
|
||||
ITEM_USE_SCRIPT_HOOK_FUNCTION(IsHoldable, "bool IsHoldable()");
|
||||
ITEM_USE_SCRIPT_HOOK_FUNCTION(OnUse, "void OnUse()");
|
||||
ITEM_USE_SCRIPT_HOOK_FUNCTION(OnPokemonUse, "void OnPokemonUse(Pokemon@ target)");
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_ANGELSCRIPTITEMUSESCRIPT_HPP
|
|
@ -150,6 +150,36 @@ CreatureLib::Battling::BattleScript* AngelScriptResolver::LoadScript(ScriptCateg
|
|||
_contextPool->ReturnContextToPool(ctx);
|
||||
return new AngelScriptScript(this, t.value(), obj, _contextPool);
|
||||
}
|
||||
|
||||
CreatureLib::Battling::ItemUseScript* AngelScriptResolver::LoadItemScript(const CreatureLib::Library::Item* item) {
|
||||
auto v = this->_itemUseScripts.TryGet(item);
|
||||
if (v.has_value()) {
|
||||
return v.value();
|
||||
}
|
||||
if (!item->GetEffect().HasValue()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto typeInfoOption = _itemUseTypes.TryGet(item->GetEffect().GetValue()->GetEffectName());
|
||||
if (!typeInfoOption.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* ctx = _contextPool->RequestContext();
|
||||
auto factory = typeInfoOption.value().get()->GetFactoryByIndex(0);
|
||||
ctx->Prepare(factory);
|
||||
auto result = ctx->Execute();
|
||||
if (result != asEXECUTION_FINISHED) {
|
||||
throw ArbUt::Exception("Instantiation failed.");
|
||||
}
|
||||
asIScriptObject* obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();
|
||||
obj->AddRef();
|
||||
auto scriptObject = new AngelScriptItemUseScript(obj, this);
|
||||
_itemUseScripts.Insert(item, scriptObject);
|
||||
_contextPool->ReturnContextToPool(ctx);
|
||||
return scriptObject;
|
||||
}
|
||||
|
||||
void AngelScriptResolver::FinalizeModule() {
|
||||
int r = _builder.BuildModule();
|
||||
if (r < 0)
|
||||
|
@ -159,8 +189,11 @@ void AngelScriptResolver::FinalizeModule() {
|
|||
std::regex variableMatcher(R"(\s*(\w+)=(\w+))", std::regex_constants::icase);
|
||||
std::smatch base_match;
|
||||
|
||||
auto pkmnScriptType = _mainModule->GetTypeInfoByName("PkmnScript");
|
||||
auto itemUseScriptType = _mainModule->GetTypeInfoByName("ItemUseScript");
|
||||
for (asUINT n = 0; n < count; n++) {
|
||||
auto typeInfo = _mainModule->GetObjectTypeByIndex(n);
|
||||
if (typeInfo->DerivesFrom(pkmnScriptType)) {
|
||||
auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId());
|
||||
for (size_t m = 0; m < metadata.size(); m++) {
|
||||
auto data = metadata[m];
|
||||
|
@ -178,33 +211,56 @@ void AngelScriptResolver::FinalizeModule() {
|
|||
effectName = ArbUt::StringView(val.c_str(), val.length());
|
||||
}
|
||||
}
|
||||
if (effectName.IsEmpty()) {
|
||||
RegisterScriptType(typeInfo, metadataKind, effectName);
|
||||
}
|
||||
}
|
||||
} else if (typeInfo->DerivesFrom(itemUseScriptType)) {
|
||||
auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId());
|
||||
for (size_t m = 0; m < metadata.size(); m++) {
|
||||
auto data = metadata[m];
|
||||
if (std::regex_match(data, base_match, metadataMatcher)) {
|
||||
auto mt = base_match[1].str();
|
||||
auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length());
|
||||
auto metadataVariables = base_match[2].str();
|
||||
if (!std::regex_match(metadataVariables, base_match, variableMatcher)) {
|
||||
continue;
|
||||
}
|
||||
ArbUt::StringView effectName;
|
||||
for (size_t variableIndex = 1; variableIndex < base_match.size(); variableIndex += 2) {
|
||||
if (ArbUt::StringView::CalculateHash(base_match[variableIndex].str().c_str()) == "effect"_cnc) {
|
||||
auto val = base_match[variableIndex + 1].str();
|
||||
effectName = ArbUt::StringView(val.c_str(), val.length());
|
||||
}
|
||||
}
|
||||
_itemUseTypes.Insert(effectName, typeInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void AngelScriptResolver::RegisterScriptType(asITypeInfo* typeInfo, const ArbUt::StringView& metadataKind,
|
||||
const ArbUt::StringView& effectName) {
|
||||
if (effectName.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
switch (metadataKind) {
|
||||
case "Move"_cnc:
|
||||
_typeDatabase[ScriptCategory::Attack].Insert(effectName,
|
||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
_typeDatabase[ScriptCategory::Attack].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
break;
|
||||
case "Pokemon"_cnc:
|
||||
_typeDatabase[ScriptCategory::Creature].Insert(effectName,
|
||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
_typeDatabase[ScriptCategory::Creature].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
break;
|
||||
case "Ability"_cnc:
|
||||
_typeDatabase[ScriptCategory::Talent].Insert(effectName,
|
||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
_typeDatabase[ScriptCategory::Talent].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
break;
|
||||
case "Status"_cnc:
|
||||
_typeDatabase[ScriptCategory::Status].Insert(effectName,
|
||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
_typeDatabase[ScriptCategory::Status].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
break;
|
||||
case "Battle"_cnc:
|
||||
_typeDatabase[ScriptCategory::Battle].Insert(effectName,
|
||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
_typeDatabase[ScriptCategory::Battle].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
break;
|
||||
case "Side"_cnc:
|
||||
_typeDatabase[ScriptCategory::Side].Insert(effectName,
|
||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
_typeDatabase[ScriptCategory::Side].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
|
||||
break;
|
||||
case "Weather"_cnc:
|
||||
_typeDatabase[static_cast<ScriptCategory>(PkmnScriptCategory::Weather)].Insert(
|
||||
|
@ -212,9 +268,6 @@ void AngelScriptResolver::FinalizeModule() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void AngelScriptResolver::CreateScript(const char* name, const char* script) {
|
||||
_builder.AddSectionFromMemory(name, script);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
#include "../../Battling/Library/BattleLibrary.hpp"
|
||||
|
||||
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT
|
||||
#include <Arbutils/StringView.hpp>
|
||||
#include <angelscript.h>
|
||||
#include "AngelScriptItemUseScript.hpp"
|
||||
#include "AngelScriptScript.hpp"
|
||||
#include "AngelScriptTypeInfo.hpp"
|
||||
|
||||
|
@ -21,10 +23,14 @@ private:
|
|||
static void Print(const std::string& str) { std::cout << str << std::endl; }
|
||||
ArbUt::Dictionary<ScriptCategory, ArbUt::Dictionary<ArbUt::StringView, AngelScriptTypeInfo*>> _typeDatabase;
|
||||
ArbUt::Dictionary<ArbUt::StringView, asITypeInfo*> _baseTypes;
|
||||
ArbUt::Dictionary<ArbUt::StringView, asITypeInfo*> _itemUseTypes;
|
||||
ArbUt::Dictionary<const CreatureLib::Library::Item*, AngelScriptItemUseScript*> _itemUseScripts;
|
||||
|
||||
void RegisterTypes();
|
||||
void
|
||||
InitializeByteCode(const ArbUt::Dictionary<ScriptCategory, ArbUt::Dictionary<ArbUt::StringView, uint32_t>>& types);
|
||||
void RegisterScriptType(asITypeInfo* typeInfo, const ArbUt::StringView& metadataKind,
|
||||
const ArbUt::StringView& effectName);
|
||||
|
||||
public:
|
||||
~AngelScriptResolver() override {
|
||||
|
@ -46,6 +52,7 @@ public:
|
|||
|
||||
CreatureLib::Battling::BattleScript* LoadScript(ScriptCategory category,
|
||||
const ArbUt::StringView& scriptName) override;
|
||||
CreatureLib::Battling::ItemUseScript* LoadItemScript(const CreatureLib::Library::Item* item) override;
|
||||
|
||||
void WriteByteCodeToFile(const char* file, bool stripDebugInfo = false);
|
||||
void LoadByteCodeFromFile(const char* file);
|
||||
|
@ -81,5 +88,7 @@ public:
|
|||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
inline ContextPool* GetContextPool() const noexcept { return _contextPool; }
|
||||
};
|
||||
#endif // PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
||||
|
|
|
@ -1,46 +1,14 @@
|
|||
#include "AngelScriptScript.hpp"
|
||||
#include "AngelScriptFunctionCall.hpp"
|
||||
#include "AngelScriptResolver.hpp"
|
||||
|
||||
#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 ArbUt::Exception(err.str()); \
|
||||
} \
|
||||
if (newContext) { \
|
||||
_ctxPool->ReturnContextToPool(ctx); \
|
||||
} else { \
|
||||
ctx->PopState(); \
|
||||
} \
|
||||
THROW("Script didn't finish properly; message " << scriptResult); \
|
||||
} \
|
||||
if (newContext) { \
|
||||
_ctxPool->ReturnContextToPool(ctx); \
|
||||
} else { \
|
||||
ctx->PopState(); \
|
||||
}
|
||||
AngelScriptUtils::AngelscriptFunctionCall( \
|
||||
s.Function, _ctxPool, _obj, GetName(), [&]([[maybe_unused]] asIScriptContext* ctx) { setup }, \
|
||||
[&]([[maybe_unused]] asIScriptContext* ctx) {});
|
||||
|
||||
CScriptArray* AngelScriptScript::GetEffectParameters(const ArbUt::List<CreatureLib::Library::EffectParameter*>& ls) {
|
||||
asITypeInfo* t = _resolver->GetBaseType("array<EffectParameter@>"_cnc);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
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"(
|
||||
int r = engine->GetModuleByIndex(0)->AddScriptSection("PkmnScript", R"(
|
||||
shared abstract class PkmnScript {
|
||||
// CreatureLib methods
|
||||
void OnInitialize(const array<EffectParameter@> &in parameters){};
|
||||
|
@ -46,4 +46,18 @@ shared abstract class PkmnScript {
|
|||
}
|
||||
)");
|
||||
Ensure(r >= 0);
|
||||
r = engine->GetModuleByIndex(0)->AddScriptSection("ItemUseScript", R"(
|
||||
shared abstract class ItemUseScript {
|
||||
bool IsItemUsable() { return false; };
|
||||
bool IsPokemonUseItem() { return false; };
|
||||
bool IsUseValidForPokemon(Pokemon@ target) { return false; };
|
||||
|
||||
bool IsHoldable() { return false; }
|
||||
|
||||
void OnUse() { };
|
||||
void OnPokemonUse(Pokemon@ target) { };
|
||||
}
|
||||
)");
|
||||
Ensure(r >= 0);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#include "../../extern/doctest.hpp"
|
||||
#include "../../src/Battling/Pokemon/CreatePokemon.hpp"
|
||||
#include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../TestLibrary/TestLibrary.hpp"
|
||||
|
||||
#define AS_CLASS(name, contents) \
|
||||
{ #name, "namespace Pokemon{ [ItemUse effect=" #name "] class " #name " : ItemUseScript { " contents "}}" }
|
||||
|
||||
static std::unordered_map<const char*, const char*> _scripts = std::unordered_map<const char*, const char*>{
|
||||
AS_CLASS(blankClass, R"(
|
||||
)"),
|
||||
|
||||
AS_CLASS(isItemUsable, R"(
|
||||
bool IsItemUsable() override {
|
||||
return true;
|
||||
}
|
||||
)"),
|
||||
|
||||
AS_CLASS(isPokemonUseItem, R"(
|
||||
bool IsPokemonUseItem() override {
|
||||
return true;
|
||||
}
|
||||
)"),
|
||||
};
|
||||
|
||||
static AngelScriptResolver* _resolverCache = nullptr;
|
||||
static AngelScriptResolver* GetScriptResolver(PkmnLib::Battling::BattleLibrary* mainLib) {
|
||||
_resolverCache = dynamic_cast<AngelScriptResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||
_resolverCache->Initialize(mainLib);
|
||||
for (auto kv : _scripts) {
|
||||
_resolverCache->CreateScript(kv.first, kv.second);
|
||||
}
|
||||
_resolverCache->FinalizeModule();
|
||||
return _resolverCache;
|
||||
}
|
||||
|
||||
static AngelScriptItemUseScript* GetScript(PkmnLib::Battling::BattleLibrary* mainLib, const ArbUt::StringView& name) {
|
||||
auto lib = GetScriptResolver(mainLib);
|
||||
auto item = CreatureLib::Library::Item(name, CreatureLib::Library::ItemCategory::MiscItem,
|
||||
CreatureLib::Library::BattleItemCategory::None, 0,
|
||||
new CreatureLib::Library::SecondaryEffect(100, name, {}), {});
|
||||
|
||||
auto s = lib->LoadItemScript(&item);
|
||||
auto script = dynamic_cast<AngelScriptItemUseScript*>(s);
|
||||
REQUIRE(script != nullptr);
|
||||
return script;
|
||||
}
|
||||
|
||||
TEST_CASE("Invoke isItemUsable item use script function on empty class") {
|
||||
auto mainLib = TestLibrary::GetLibrary();
|
||||
|
||||
auto script = GetScript(mainLib, "blankClass"_cnc);
|
||||
REQUIRE(script != nullptr);
|
||||
REQUIRE_FALSE(script->IsItemUsable());
|
||||
|
||||
delete script;
|
||||
}
|
||||
TEST_CASE("Invoke isItemUsable item use script function") {
|
||||
auto mainLib = TestLibrary::GetLibrary();
|
||||
|
||||
auto script = GetScript(mainLib, "isItemUsable"_cnc);
|
||||
REQUIRE(script != nullptr);
|
||||
REQUIRE(script->IsItemUsable());
|
||||
|
||||
delete script;
|
||||
}
|
||||
|
||||
TEST_CASE("Invoke isPokemonUseItem item use script function on empty class") {
|
||||
auto mainLib = TestLibrary::GetLibrary();
|
||||
|
||||
auto script = GetScript(mainLib, "blankClass"_cnc);
|
||||
REQUIRE(script != nullptr);
|
||||
REQUIRE_FALSE(script->IsCreatureUseItem());
|
||||
|
||||
delete script;
|
||||
}
|
||||
TEST_CASE("Invoke isPokemonUseItem item use script function") {
|
||||
auto mainLib = TestLibrary::GetLibrary();
|
||||
|
||||
auto script = GetScript(mainLib, "isPokemonUseItem"_cnc);
|
||||
REQUIRE(script != nullptr);
|
||||
REQUIRE(script->IsCreatureUseItem());
|
||||
|
||||
delete script;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -8,7 +8,7 @@ static std::unordered_map<const char*, const char*> _scripts =
|
|||
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||
namespace Pokemon{
|
||||
[Pokemon effect=testScript1]
|
||||
class testScript1 {
|
||||
class testScript1 : PkmnScript {
|
||||
bool testSpecies(Pokemon@ p, const Species@ species){ return p.Species is species; }
|
||||
bool testForme(Pokemon@ p, const Forme@ forme){ return p.Forme is forme; }
|
||||
bool testLevel(Pokemon@ p, uint8 level){ return p.Level == level; }
|
||||
|
|
|
@ -7,7 +7,7 @@ static std::unordered_map<const char*, const char*> _scripts =
|
|||
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||
namespace Pokemon{
|
||||
[Pokemon effect=testScript1]
|
||||
class testScript1 {
|
||||
class testScript1 : PkmnScript {
|
||||
bool testName(const Forme@ s, const constString &in name){ return s.Name == name; }
|
||||
bool testWeight(const Forme@ s, float weight){ return s.Weight == weight; }
|
||||
bool testHeight(const Forme@ s, float height){ return s.Height == height; }
|
||||
|
|
|
@ -7,7 +7,7 @@ static std::unordered_map<const char*, const char*> _scripts =
|
|||
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||
namespace Pokemon{
|
||||
[Pokemon effect=testScript1]
|
||||
class testScript1 {
|
||||
class testScript1 : PkmnScript {
|
||||
bool testName(const Item@ i, const constString &in name){ return i.Name == name; }
|
||||
bool testCategory(const Item@ i, ItemCategory category){ return i.Category == category; }
|
||||
bool testBattleCategory(const Item@ i, BattleItemCategory category){ return i.BattleCategory == category; }
|
||||
|
|
|
@ -7,7 +7,7 @@ static std::unordered_map<const char*, const char*> _scripts =
|
|||
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||
namespace Pokemon{
|
||||
[Pokemon effect=testScript1]
|
||||
class testScript1 {
|
||||
class testScript1 : PkmnScript {
|
||||
bool testName(const MoveData@ s, const constString &in name){ return s.Name == name; }
|
||||
bool testType(const MoveData@ s, uint8 type){ return s.Type == type; }
|
||||
bool testCategory(const MoveData@ s, MoveCategory category){ return s.Category == category; }
|
||||
|
|
|
@ -7,7 +7,7 @@ static std::unordered_map<const char*, const char*> _scripts =
|
|||
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||
namespace Pokemon{
|
||||
[Pokemon effect=testScript1]
|
||||
class testScript1 {
|
||||
class testScript1 : PkmnScript {
|
||||
bool testName(const Species@ s, const constString &in name){ return s.Name == name; }
|
||||
bool testId(const Species@ s, uint16 id){ return s.Id == id; }
|
||||
bool testGenderRate(const Species@ s, float rate){ return s.GenderRate == rate; }
|
||||
|
|
|
@ -7,7 +7,7 @@ static std::unordered_map<const char*, const char*> _scripts =
|
|||
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||
namespace Pokemon{
|
||||
[Pokemon effect=testScript1]
|
||||
class testScript1 {
|
||||
class testScript1 : PkmnScript {
|
||||
bool testMaximumLevel(const StaticLibrary@ s, uint8 level){ return s.Settings.MaximalLevel == level; }
|
||||
bool testMaximumMoves(const StaticLibrary@ s, uint8 moveCount){ return s.Settings.MaximalMoves == moveCount; }
|
||||
bool testSpeciesLibrary(const StaticLibrary@ s, const SpeciesLibrary@ speciesLib){ return s.SpeciesLibrary is speciesLib; }
|
||||
|
|
|
@ -55,6 +55,6 @@ PkmnLib::Library::ItemLibrary* TestLibrary::BuildItemLibrary() {
|
|||
auto lib = new PkmnLib::Library::ItemLibrary();
|
||||
lib->Insert("testItem"_cnc.GetHash(),
|
||||
new PkmnLib::Library::Item("testItem"_cnc, CreatureLib::Library::ItemCategory::MiscItem,
|
||||
CreatureLib::Library::BattleItemCategory::None, 0, {}, 0));
|
||||
CreatureLib::Library::BattleItemCategory::None, 0, nullptr, {}, 0));
|
||||
return lib;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue