bool SetNodeLocalRotationMatrix(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* object = ResolveNode(obj, nodeName, firstPerson);
		if(object && inArray.Length() == 9) {
			inArray.Get(&object->m_localTransform.rot.data[0][0], 0);
			inArray.Get(&object->m_localTransform.rot.data[0][1], 1);
			inArray.Get(&object->m_localTransform.rot.data[0][2], 2);

			inArray.Get(&object->m_localTransform.rot.data[1][0], 3);
			inArray.Get(&object->m_localTransform.rot.data[1][1], 4);
			inArray.Get(&object->m_localTransform.rot.data[1][2], 5);

			inArray.Get(&object->m_localTransform.rot.data[2][0], 6);
			inArray.Get(&object->m_localTransform.rot.data[2][1], 7);
			inArray.Get(&object->m_localTransform.rot.data[2][2], 8);

			BSTaskPool * taskPool = BSTaskPool::GetSingleton();
			if(taskPool)
				taskPool->UpdateWorldData(object);

			return true;
		}

		return false;
	}
	bool SetNodeLocalRotationEuler(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* object = ResolveNode(obj, nodeName, firstPerson);
		if(object && inArray.Length() == 3) {
			float heading, attitude, bank;

			inArray.Get(&heading, 0);
			inArray.Get(&attitude, 1);
			inArray.Get(&bank, 2);

			// Degrees to radians
			heading *= MATH_PI / 180;
			attitude *= MATH_PI / 180;
			bank *= MATH_PI / 180;

			object->m_localTransform.rot.SetEulerAngles(heading, attitude, bank);

			BSTaskPool * taskPool = BSTaskPool::GetSingleton();
			if(taskPool)
				taskPool->UpdateWorldData(object);

			return true;
		}

		return false;
	}
	bool GetNodeLocalPosition(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* object = ResolveNode(obj, nodeName, firstPerson);
		if(object && inArray.Length() == 3) {
			inArray.Set(&object->m_localTransform.pos.x, 0);
			inArray.Set(&object->m_localTransform.pos.y, 1);
			inArray.Set(&object->m_localTransform.pos.z, 2);
			return true;
		}

		return false;
	}
	bool GetRelativeNodePosition(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeNameA, BSFixedString nodeNameB, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* objectA = ResolveNode(obj, nodeNameA, firstPerson);
		NiAVObject	* objectB = ResolveNode(obj, nodeNameB, firstPerson);
		if(objectA && objectB && inArray.Length() == 3) {
			float x = objectB->m_worldTransform.pos.x - objectA->m_worldTransform.pos.x;
			float y = objectB->m_worldTransform.pos.y - objectA->m_worldTransform.pos.y;
			float z = objectB->m_worldTransform.pos.z - objectA->m_worldTransform.pos.z;
			inArray.Set(&x, 0);
			inArray.Set(&y, 1);
			inArray.Set(&z, 2);
			return true;
		}

		return false;
	}
	bool SetNodeLocalPosition(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* object = ResolveNode(obj, nodeName, firstPerson);
		if(object && inArray.Length() == 3) {
			inArray.Get(&object->m_localTransform.pos.x, 0);
			inArray.Get(&object->m_localTransform.pos.y, 1);
			inArray.Get(&object->m_localTransform.pos.z, 2);

			BSTaskPool * taskPool = BSTaskPool::GetSingleton();
			if(taskPool)
				taskPool->UpdateWorldData(object);

			return true;
		}

		return false;
	}
	void CreateEnchantment(TESForm* baseForm, BaseExtraList * extraData, float maxCharge, VMArray<EffectSetting*> effects, VMArray<float> magnitudes, VMArray<UInt32> areas, VMArray<UInt32> durations)
	{
		if(baseForm && (baseForm->formType == TESObjectWEAP::kTypeID || baseForm->formType == TESObjectARMO::kTypeID)) {
			EnchantmentItem * enchantment = NULL;
			if(effects.Length() > 0 && magnitudes.Length() == effects.Length() && areas.Length() == effects.Length() && durations.Length() == effects.Length()) {
				tArray<MagicItem::EffectItem> effectItems;
				effectItems.Allocate(effects.Length());

				UInt32 j = 0;
				for(UInt32 i = 0; i < effects.Length(); i++) {
					EffectSetting * magicEffect = NULL;
					effects.Get(&magicEffect, i);
					if(magicEffect) { // Only add effects that actually exist
						magnitudes.Get(&effectItems[j].magnitude, i);
						areas.Get(&effectItems[j].area, i);
						durations.Get(&effectItems[j].duration, i);
						effectItems[j].mgef = magicEffect;
						j++;
					}
				}
				effectItems.count = j; // Set count to existing count

				if(baseForm->formType == TESObjectWEAP::kTypeID)
					enchantment = CALL_MEMBER_FN(PersistentFormManager::GetSingleton(), CreateOffensiveEnchantment)(&effectItems);
				else
					enchantment = CALL_MEMBER_FN(PersistentFormManager::GetSingleton(), CreateDefensiveEnchantment)(&effectItems);

				FormHeap_Free(effectItems.arr.entries);
			}

			if(enchantment) {
				if(maxCharge > 0xFFFF) // Charge exceeds uint16 clip it
					maxCharge = 0xFFFF;

				ExtraEnchantment* extraEnchant = static_cast<ExtraEnchantment*>(extraData->GetByType(kExtraData_Enchantment));
				if(extraEnchant) {
					PersistentFormManager::GetSingleton()->DecRefEnchantment(extraEnchant->enchant);
					extraEnchant->enchant = enchantment;
					PersistentFormManager::GetSingleton()->IncRefEnchantment(extraEnchant->enchant);

					extraEnchant->maxCharge = (UInt16)maxCharge;
				} else {
					ExtraEnchantment* extraEnchant = ExtraEnchantment::Create();
					extraEnchant->enchant = enchantment;
					extraEnchant->maxCharge = (UInt16)maxCharge;
					extraData->Add(kExtraData_Enchantment, extraEnchant);
				}
			}
		}
	}
	bool GetNodeWorldRotationMatrix(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* object = ResolveNode(obj, nodeName, firstPerson);
		if(object && inArray.Length() == 9) {
			inArray.Set(&object->m_worldTransform.rot.data[0][0], 0);
			inArray.Set(&object->m_worldTransform.rot.data[0][1], 1);
			inArray.Set(&object->m_worldTransform.rot.data[0][2], 2);

			inArray.Set(&object->m_worldTransform.rot.data[1][0], 3);
			inArray.Set(&object->m_worldTransform.rot.data[1][1], 4);
			inArray.Set(&object->m_worldTransform.rot.data[1][2], 5);

			inArray.Set(&object->m_worldTransform.rot.data[2][0], 6);
			inArray.Set(&object->m_worldTransform.rot.data[2][1], 7);
			inArray.Set(&object->m_worldTransform.rot.data[2][2], 8);
			return true;
		}

		return false;
	}
	bool GetNodeWorldRotationEuler(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, VMArray<float> inArray, bool firstPerson)
	{
		NiAVObject	* object = ResolveNode(obj, nodeName, firstPerson);
		if(object && inArray.Length() == 3) {
			float heading, attitude, bank;
			object->m_localTransform.rot.GetEulerAngles(&heading, &attitude, &bank);

			// Radians to degrees
			heading *= 180 / MATH_PI;
			attitude *= 180 / MATH_PI;
			bank *= 180 / MATH_PI;

			inArray.Set(&heading, 0);
			inArray.Set(&attitude, 1);
			inArray.Set(&bank, 2);
			return true;
		}

		return false;
	}
	void PushArgs(VMArray<T> args)
	{
		using namespace papyrusUI;

		UInt32 argCount = args.Length();

		UInt32 offset = invokeDelegate_->argCount;
		UInt32 newArgCount = offset + argCount;

		if (newArgCount > 128)
		{
			_WARNING("Tried to push more than 128 arguments into PapyrusUICallback.");
			return;
		}
		
		invokeDelegate_->argCount = newArgCount;
		for (UInt32 i=0; i<argCount; i++, offset++)
		{
			T arg;
			args.Get(&arg, i);
			SetGFxValue<T>(&invokeDelegate_->args[offset], arg);
		}
	}
bool EAr_MGEFInfoLib::SetMainArrays(
	VMArray<TESEffectShader*>	eShaders,
	VMArray<BGSArtObject*>		eArt,
	VMArray<TESEffectShader*>	hShaders,
	VMArray<BGSArtObject*>		hArt,
	VMArray<BGSProjectile*>		projectiles,
	VMArray<BGSImpactDataSet*>	impactData,
	VMArray<UInt32>				persistFlags,
	VMArray<float>				tWeights,
	VMArray<float>				tCurves,
	VMArray<float>				tDurations)
{
	if ((eShaders.Length() < 126) ||
		(eArt.Length() < 126) ||
		(hShaders.Length() < 126) ||
		(hArt.Length() < 126) ||
		(projectiles.Length() < 126) ||
		(impactData.Length() < 126) ||
		(persistFlags.Length() < 126) ||
		(tWeights.Length() < 126) ||
		(tCurves.Length() < 126) ||
		(tDurations.Length() < 126))
		return false;

	for (UInt32 i = 0; i < 126; i++)
	{
		TESEffectShader*  pESh = NULL;
		BGSArtObject*     pEAr = NULL;
		TESEffectShader*  pHSh = NULL;
		BGSArtObject*     pHAr = NULL;
		BGSProjectile*    pPro = NULL;
		BGSImpactDataSet* pIDS = NULL;
		UInt32            flag;
		float             tWei;
		float             tCur;
		float             tDur;

		eShaders.Get		(&pESh, i);
		eArt.Get			(&pEAr, i);
		hShaders.Get		(&pHSh, i);
		hArt.Get			(&pHAr, i);
		projectiles.Get		(&pPro, i);
		impactData.Get		(&pIDS, i);
		persistFlags.Get	(&flag, i);
		tWeights.Get		(&tWei, i);
		tCurves.Get			(&tCur, i);
		tDurations.Get		(&tDur, i);

		_eShaders.push_back		(pESh);
		_eArt.push_back			(pEAr);
		_hShaders.push_back		(pHSh);
		_hArt.push_back			(pHAr);
		_projectiles.push_back	(pPro);
		_impactData.push_back	(pIDS);
		_persistFlags.push_back	(flag);
		_tWeights.push_back		(tWei);
		_tCurves.push_back		(tCur);
		_tDurations.push_back	(tDur);
	}

	battleaxeKeyword = papyrusKeyword::GetKeyword(NULL, "WeapTypeBattleaxe");
	READY = true;
	_MESSAGE("Internal effect library loaded successfully.");
	return true;
}