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,
|
export Item* PkmnLib_Item_Construct(const char* name, CreatureLib::Library::ItemCategory category,
|
||||||
CreatureLib::Library::BattleItemCategory battleCategory, int32_t price,
|
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);
|
std::unordered_set<uint32_t> conversedFlags(flagsCount);
|
||||||
for (size_t i = 0; i < flagsCount; i++) {
|
for (size_t i = 0; i < flagsCount; i++) {
|
||||||
conversedFlags.insert(ArbUt::StringView::CalculateHash(flags[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; }
|
export void PkmnLib_Item_Destruct(const Item* p) { delete p; }
|
||||||
|
|
|
@ -9,8 +9,10 @@ namespace PkmnLib::Library {
|
||||||
public:
|
public:
|
||||||
Item(const ArbUt::StringView& name, CreatureLib::Library::ItemCategory category,
|
Item(const ArbUt::StringView& name, CreatureLib::Library::ItemCategory category,
|
||||||
CreatureLib::Library::BattleItemCategory battleCategory, int32_t price,
|
CreatureLib::Library::BattleItemCategory battleCategory, int32_t price,
|
||||||
const std::unordered_set<uint32_t>& flags, uint8_t flingPower) noexcept
|
const CreatureLib::Library::SecondaryEffect* effect, const std::unordered_set<uint32_t>& flags,
|
||||||
: CreatureLib::Library::Item(name, category, battleCategory, price, flags), _flingPower(flingPower) {}
|
uint8_t flingPower) noexcept
|
||||||
|
: CreatureLib::Library::Item(name, category, battleCategory, price, effect, flags),
|
||||||
|
_flingPower(flingPower) {}
|
||||||
|
|
||||||
inline uint8_t GetFlingPower() const noexcept { return _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
|
|
@ -127,7 +127,7 @@ void AngelScriptResolver::MessageCallback(const asSMessageInfo* msg, void*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureLib::Battling::BattleScript* AngelScriptResolver::LoadScript(ScriptCategory category,
|
CreatureLib::Battling::BattleScript* AngelScriptResolver::LoadScript(ScriptCategory category,
|
||||||
const ArbUt::StringView& scriptName) {
|
const ArbUt::StringView& scriptName) {
|
||||||
ArbUt::Dictionary<ArbUt::StringView, AngelScriptTypeInfo*> innerDb;
|
ArbUt::Dictionary<ArbUt::StringView, AngelScriptTypeInfo*> innerDb;
|
||||||
auto v = _typeDatabase.TryGet(category);
|
auto v = _typeDatabase.TryGet(category);
|
||||||
if (!v.has_value()) {
|
if (!v.has_value()) {
|
||||||
|
@ -150,6 +150,36 @@ CreatureLib::Battling::BattleScript* AngelScriptResolver::LoadScript(ScriptCateg
|
||||||
_contextPool->ReturnContextToPool(ctx);
|
_contextPool->ReturnContextToPool(ctx);
|
||||||
return new AngelScriptScript(this, t.value(), obj, _contextPool);
|
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() {
|
void AngelScriptResolver::FinalizeModule() {
|
||||||
int r = _builder.BuildModule();
|
int r = _builder.BuildModule();
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -159,62 +189,85 @@ void AngelScriptResolver::FinalizeModule() {
|
||||||
std::regex variableMatcher(R"(\s*(\w+)=(\w+))", std::regex_constants::icase);
|
std::regex variableMatcher(R"(\s*(\w+)=(\w+))", std::regex_constants::icase);
|
||||||
std::smatch base_match;
|
std::smatch base_match;
|
||||||
|
|
||||||
|
auto pkmnScriptType = _mainModule->GetTypeInfoByName("PkmnScript");
|
||||||
|
auto itemUseScriptType = _mainModule->GetTypeInfoByName("ItemUseScript");
|
||||||
for (asUINT n = 0; n < count; n++) {
|
for (asUINT n = 0; n < count; n++) {
|
||||||
auto typeInfo = _mainModule->GetObjectTypeByIndex(n);
|
auto typeInfo = _mainModule->GetObjectTypeByIndex(n);
|
||||||
auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId());
|
if (typeInfo->DerivesFrom(pkmnScriptType)) {
|
||||||
for (size_t m = 0; m < metadata.size(); m++) {
|
auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId());
|
||||||
auto data = metadata[m];
|
for (size_t m = 0; m < metadata.size(); m++) {
|
||||||
if (std::regex_match(data, base_match, metadataMatcher)) {
|
auto data = metadata[m];
|
||||||
auto mt = base_match[1].str();
|
if (std::regex_match(data, base_match, metadataMatcher)) {
|
||||||
auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length());
|
auto mt = base_match[1].str();
|
||||||
auto metadataVariables = base_match[2].str();
|
auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length());
|
||||||
if (!std::regex_match(metadataVariables, base_match, variableMatcher)) {
|
auto metadataVariables = base_match[2].str();
|
||||||
continue;
|
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());
|
|
||||||
}
|
}
|
||||||
|
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;
|
} else if (typeInfo->DerivesFrom(itemUseScriptType)) {
|
||||||
}
|
auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId());
|
||||||
switch (metadataKind) {
|
for (size_t m = 0; m < metadata.size(); m++) {
|
||||||
case "Move"_cnc:
|
auto data = metadata[m];
|
||||||
_typeDatabase[ScriptCategory::Attack].Insert(effectName,
|
if (std::regex_match(data, base_match, metadataMatcher)) {
|
||||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
auto mt = base_match[1].str();
|
||||||
break;
|
auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length());
|
||||||
case "Pokemon"_cnc:
|
auto metadataVariables = base_match[2].str();
|
||||||
_typeDatabase[ScriptCategory::Creature].Insert(effectName,
|
if (!std::regex_match(metadataVariables, base_match, variableMatcher)) {
|
||||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
continue;
|
||||||
break;
|
}
|
||||||
case "Ability"_cnc:
|
ArbUt::StringView effectName;
|
||||||
_typeDatabase[ScriptCategory::Talent].Insert(effectName,
|
for (size_t variableIndex = 1; variableIndex < base_match.size(); variableIndex += 2) {
|
||||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
if (ArbUt::StringView::CalculateHash(base_match[variableIndex].str().c_str()) == "effect"_cnc) {
|
||||||
break;
|
auto val = base_match[variableIndex + 1].str();
|
||||||
case "Status"_cnc:
|
effectName = ArbUt::StringView(val.c_str(), val.length());
|
||||||
_typeDatabase[ScriptCategory::Status].Insert(effectName,
|
}
|
||||||
new AngelScriptTypeInfo(effectName, typeInfo));
|
}
|
||||||
break;
|
_itemUseTypes.Insert(effectName, typeInfo);
|
||||||
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::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) {
|
void AngelScriptResolver::CreateScript(const char* name, const char* script) {
|
||||||
_builder.AddSectionFromMemory(name, script);
|
_builder.AddSectionFromMemory(name, script);
|
||||||
}
|
}
|
||||||
|
@ -325,4 +378,4 @@ void AngelScriptResolver::InitializeByteCode(
|
||||||
typeDatabase.Set(innerDb.first, newInnerDb);
|
typeDatabase.Set(innerDb.first, newInnerDb);
|
||||||
}
|
}
|
||||||
_typeDatabase = typeDatabase;
|
_typeDatabase = typeDatabase;
|
||||||
}
|
}
|
|
@ -6,7 +6,9 @@
|
||||||
#include "../../Battling/Library/BattleLibrary.hpp"
|
#include "../../Battling/Library/BattleLibrary.hpp"
|
||||||
|
|
||||||
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT
|
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT
|
||||||
|
#include <Arbutils/StringView.hpp>
|
||||||
#include <angelscript.h>
|
#include <angelscript.h>
|
||||||
|
#include "AngelScriptItemUseScript.hpp"
|
||||||
#include "AngelScriptScript.hpp"
|
#include "AngelScriptScript.hpp"
|
||||||
#include "AngelScriptTypeInfo.hpp"
|
#include "AngelScriptTypeInfo.hpp"
|
||||||
|
|
||||||
|
@ -21,10 +23,14 @@ private:
|
||||||
static void Print(const std::string& str) { std::cout << str << std::endl; }
|
static void Print(const std::string& str) { std::cout << str << std::endl; }
|
||||||
ArbUt::Dictionary<ScriptCategory, ArbUt::Dictionary<ArbUt::StringView, AngelScriptTypeInfo*>> _typeDatabase;
|
ArbUt::Dictionary<ScriptCategory, ArbUt::Dictionary<ArbUt::StringView, AngelScriptTypeInfo*>> _typeDatabase;
|
||||||
ArbUt::Dictionary<ArbUt::StringView, asITypeInfo*> _baseTypes;
|
ArbUt::Dictionary<ArbUt::StringView, asITypeInfo*> _baseTypes;
|
||||||
|
ArbUt::Dictionary<ArbUt::StringView, asITypeInfo*> _itemUseTypes;
|
||||||
|
ArbUt::Dictionary<const CreatureLib::Library::Item*, AngelScriptItemUseScript*> _itemUseScripts;
|
||||||
|
|
||||||
void RegisterTypes();
|
void RegisterTypes();
|
||||||
void
|
void
|
||||||
InitializeByteCode(const ArbUt::Dictionary<ScriptCategory, ArbUt::Dictionary<ArbUt::StringView, uint32_t>>& types);
|
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:
|
public:
|
||||||
~AngelScriptResolver() override {
|
~AngelScriptResolver() override {
|
||||||
|
@ -46,6 +52,7 @@ public:
|
||||||
|
|
||||||
CreatureLib::Battling::BattleScript* LoadScript(ScriptCategory category,
|
CreatureLib::Battling::BattleScript* LoadScript(ScriptCategory category,
|
||||||
const ArbUt::StringView& scriptName) override;
|
const ArbUt::StringView& scriptName) override;
|
||||||
|
CreatureLib::Battling::ItemUseScript* LoadItemScript(const CreatureLib::Library::Item* item) override;
|
||||||
|
|
||||||
void WriteByteCodeToFile(const char* file, bool stripDebugInfo = false);
|
void WriteByteCodeToFile(const char* file, bool stripDebugInfo = false);
|
||||||
void LoadByteCodeFromFile(const char* file);
|
void LoadByteCodeFromFile(const char* file);
|
||||||
|
@ -81,5 +88,7 @@ public:
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline ContextPool* GetContextPool() const noexcept { return _contextPool; }
|
||||||
};
|
};
|
||||||
#endif // PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
#endif // PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
||||||
|
|
|
@ -1,46 +1,14 @@
|
||||||
#include "AngelScriptScript.hpp"
|
#include "AngelScriptScript.hpp"
|
||||||
|
#include "AngelScriptFunctionCall.hpp"
|
||||||
#include "AngelScriptResolver.hpp"
|
#include "AngelScriptResolver.hpp"
|
||||||
|
|
||||||
#define CALL_HOOK(name, setup) \
|
#define CALL_HOOK(name, setup) \
|
||||||
auto s = _type->Get##name(); \
|
auto s = _type->Get##name(); \
|
||||||
if (!s.Exists) \
|
if (!s.Exists) \
|
||||||
return; \
|
return; \
|
||||||
auto ctx = asGetActiveContext(); \
|
AngelScriptUtils::AngelscriptFunctionCall( \
|
||||||
bool newContext = false; \
|
s.Function, _ctxPool, _obj, GetName(), [&]([[maybe_unused]] asIScriptContext* ctx) { setup }, \
|
||||||
if (ctx == nullptr) { \
|
[&]([[maybe_unused]] asIScriptContext* ctx) {});
|
||||||
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(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptArray* AngelScriptScript::GetEffectParameters(const ArbUt::List<CreatureLib::Library::EffectParameter*>& ls) {
|
CScriptArray* AngelScriptScript::GetEffectParameters(const ArbUt::List<CreatureLib::Library::EffectParameter*>& ls) {
|
||||||
asITypeInfo* t = _resolver->GetBaseType("array<EffectParameter@>"_cnc);
|
asITypeInfo* t = _resolver->GetBaseType("array<EffectParameter@>"_cnc);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
void BasicScriptClass::Register(asIScriptEngine* engine) {
|
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
|
// 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.
|
// 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 {
|
shared abstract class PkmnScript {
|
||||||
// CreatureLib methods
|
// CreatureLib methods
|
||||||
void OnInitialize(const array<EffectParameter@> &in parameters){};
|
void OnInitialize(const array<EffectParameter@> &in parameters){};
|
||||||
|
@ -46,4 +46,18 @@ shared abstract class PkmnScript {
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
Ensure(r >= 0);
|
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"(
|
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||||
namespace Pokemon{
|
namespace Pokemon{
|
||||||
[Pokemon effect=testScript1]
|
[Pokemon effect=testScript1]
|
||||||
class testScript1 {
|
class testScript1 : PkmnScript {
|
||||||
bool testSpecies(Pokemon@ p, const Species@ species){ return p.Species is species; }
|
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 testForme(Pokemon@ p, const Forme@ forme){ return p.Forme is forme; }
|
||||||
bool testLevel(Pokemon@ p, uint8 level){ return p.Level == level; }
|
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"(
|
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||||
namespace Pokemon{
|
namespace Pokemon{
|
||||||
[Pokemon effect=testScript1]
|
[Pokemon effect=testScript1]
|
||||||
class testScript1 {
|
class testScript1 : PkmnScript {
|
||||||
bool testName(const Forme@ s, const constString &in name){ return s.Name == name; }
|
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 testWeight(const Forme@ s, float weight){ return s.Weight == weight; }
|
||||||
bool testHeight(const Forme@ s, float height){ return s.Height == height; }
|
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"(
|
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||||
namespace Pokemon{
|
namespace Pokemon{
|
||||||
[Pokemon effect=testScript1]
|
[Pokemon effect=testScript1]
|
||||||
class testScript1 {
|
class testScript1 : PkmnScript {
|
||||||
bool testName(const Item@ i, const constString &in name){ return i.Name == name; }
|
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 testCategory(const Item@ i, ItemCategory category){ return i.Category == category; }
|
||||||
bool testBattleCategory(const Item@ i, BattleItemCategory category){ return i.BattleCategory == 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"(
|
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||||
namespace Pokemon{
|
namespace Pokemon{
|
||||||
[Pokemon effect=testScript1]
|
[Pokemon effect=testScript1]
|
||||||
class testScript1 {
|
class testScript1 : PkmnScript {
|
||||||
bool testName(const MoveData@ s, const constString &in name){ return s.Name == name; }
|
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 testType(const MoveData@ s, uint8 type){ return s.Type == type; }
|
||||||
bool testCategory(const MoveData@ s, MoveCategory category){ return s.Category == category; }
|
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"(
|
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||||
namespace Pokemon{
|
namespace Pokemon{
|
||||||
[Pokemon effect=testScript1]
|
[Pokemon effect=testScript1]
|
||||||
class testScript1 {
|
class testScript1 : PkmnScript {
|
||||||
bool testName(const Species@ s, const constString &in name){ return s.Name == name; }
|
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 testId(const Species@ s, uint16 id){ return s.Id == id; }
|
||||||
bool testGenderRate(const Species@ s, float rate){ return s.GenderRate == rate; }
|
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"(
|
std::unordered_map<const char*, const char*>{{"testScript1", R"(
|
||||||
namespace Pokemon{
|
namespace Pokemon{
|
||||||
[Pokemon effect=testScript1]
|
[Pokemon effect=testScript1]
|
||||||
class testScript1 {
|
class testScript1 : PkmnScript {
|
||||||
bool testMaximumLevel(const StaticLibrary@ s, uint8 level){ return s.Settings.MaximalLevel == level; }
|
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 testMaximumMoves(const StaticLibrary@ s, uint8 moveCount){ return s.Settings.MaximalMoves == moveCount; }
|
||||||
bool testSpeciesLibrary(const StaticLibrary@ s, const SpeciesLibrary@ speciesLib){ return s.SpeciesLibrary is speciesLib; }
|
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();
|
auto lib = new PkmnLib::Library::ItemLibrary();
|
||||||
lib->Insert("testItem"_cnc.GetHash(),
|
lib->Insert("testItem"_cnc.GetHash(),
|
||||||
new PkmnLib::Library::Item("testItem"_cnc, CreatureLib::Library::ItemCategory::MiscItem,
|
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;
|
return lib;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue