Initial support for item use scripts in angelscript.
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2021-03-07 17:09:30 +01:00
parent 18cebe842a
commit 27dd8a8202
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
17 changed files with 405 additions and 97 deletions

View File

@ -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; }

View File

@ -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; }
};

View File

@ -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

View File

@ -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) {});
}

View File

@ -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

View File

@ -127,7 +127,7 @@ void AngelScriptResolver::MessageCallback(const asSMessageInfo* msg, void*) {
}
CreatureLib::Battling::BattleScript* AngelScriptResolver::LoadScript(ScriptCategory category,
const ArbUt::StringView& scriptName) {
const ArbUt::StringView& scriptName) {
ArbUt::Dictionary<ArbUt::StringView, AngelScriptTypeInfo*> innerDb;
auto v = _typeDatabase.TryGet(category);
if (!v.has_value()) {
@ -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,62 +189,85 @@ 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);
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());
if (typeInfo->DerivesFrom(pkmnScriptType)) {
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());
}
}
RegisterScriptType(typeInfo, metadataKind, effectName);
}
if (effectName.IsEmpty()) {
continue;
}
switch (metadataKind) {
case "Move"_cnc:
_typeDatabase[ScriptCategory::Attack].Insert(effectName,
new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Pokemon"_cnc:
_typeDatabase[ScriptCategory::Creature].Insert(effectName,
new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Ability"_cnc:
_typeDatabase[ScriptCategory::Talent].Insert(effectName,
new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Status"_cnc:
_typeDatabase[ScriptCategory::Status].Insert(effectName,
new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Battle"_cnc:
_typeDatabase[ScriptCategory::Battle].Insert(effectName,
new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Side"_cnc:
_typeDatabase[ScriptCategory::Side].Insert(effectName,
new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Weather"_cnc:
_typeDatabase[static_cast<ScriptCategory>(PkmnScriptCategory::Weather)].Insert(
effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
}
} 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));
break;
case "Pokemon"_cnc:
_typeDatabase[ScriptCategory::Creature].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Ability"_cnc:
_typeDatabase[ScriptCategory::Talent].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Status"_cnc:
_typeDatabase[ScriptCategory::Status].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Battle"_cnc:
_typeDatabase[ScriptCategory::Battle].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Side"_cnc:
_typeDatabase[ScriptCategory::Side].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
case "Weather"_cnc:
_typeDatabase[static_cast<ScriptCategory>(PkmnScriptCategory::Weather)].Insert(
effectName, new AngelScriptTypeInfo(effectName, typeInfo));
break;
}
}
void AngelScriptResolver::CreateScript(const char* name, const char* script) {
_builder.AddSectionFromMemory(name, script);
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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;
}