#pragma once #include "Util.h" #include "PersistentFormManager.h" #include "Patches/DialogueMenuPatch.h" namespace Papyrus::PapyrusFunctions { inline RE::AlchemyItem* CreatePotion(RE::StaticFunctionTag*, std::vector effects, std::vector magnitudes, std::vector areas, std::vector durations, uint32_t arraySize) { RE::AlchemyItem* result = NULL; if (effects.size() >= arraySize && magnitudes.size() >= arraySize && areas.size() >= arraySize && durations.size() >= arraySize) { RE::tArray effectItems; effectItems.Allocate(effects.size()); uint16_t count = 0; for (uint16_t i = 0; i < effects.size(); ++i) { if (effects[i]) { effectItems[i].baseEffect = effects[i]; effectItems[i].effectItem.duration = durations[i]; effectItems[i].effectItem.magnitude = magnitudes[i]; effectItems[i].effectItem.area = areas[i]; count++; } } effectItems.count = count; RE::PersistentFormManager::GetSingleton()->CreatePotion(&result, &effectItems); effectItems.Clear(); } else { logger::warn("Illegal arrays for creating a potion"); } return result; } inline uint8_t GetNewGameCount(RE::StaticFunctionTag*) { return NewGameCount(); } inline RE::TESObjectREFR* GetCurrentContainer(RE::StaticFunctionTag*) { const auto handle = RE::ContainerMenu::GetTargetRefHandle(); const auto refr = RE::TESObjectREFR::LookupByHandle(handle); return refr ? refr.get() : nullptr; } inline std::vector GetPlayerFollowers(RE::StaticFunctionTag*) { std::vector result; if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) { for (auto& actorHandle : processLists->highActorHandles) { if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate() && !actor->IsDead() && !actor->IsDisabled()) { result.push_back(actor.get()); } } } return result; } RE::BSFixedString GetAutosaveName(RE::StaticFunctionTag*, std::int32_t a_index) { /* Save Name Structure (from NQS NamedQuicksaves by Ryan McKenzie) Save3_0C2D58E2_0_507269736F6E6572_Tamriel_000002_20180503063315_4_1.ess Save3: Type and index of save 0C2D58E2: Unique hash used to identify your save profile. Regenerated on closing racemenu. 0: Flag for modded game. 507269736F6E6572: Character name in hex. Tamriel: coc code. 000002: Days, hours, minutes played. 20180503063315: Year, month, day, hour, minute, second in GMT + 0. 4: Player level. 1: Unknown flag. */ std::stringstream sstream; sstream << "Autosave0"; sstream << a_index; sstream << "_"; sstream << GetPlayerHash().c_str(); sstream << "_0_"; sstream << StringToHex(RE::PlayerCharacter::GetSingleton()->GetName()).c_str(); sstream << "_"; RE::TESObjectCELL* parentCell = RE::PlayerCharacter::GetSingleton()->parentCell; if (parentCell->IsInteriorCell()) { sstream << parentCell->GetFormEditorID(); } else { sstream << RE::PlayerCharacter::GetSingleton()->GetWorldspace()->GetFormEditorID(); } sstream << "_000000_00000000000000_1_1"; return sstream.str(); } void DisableDialogueQuitting(RE::StaticFunctionTag*) { DialogueMenuPatch::BlockTab(); } void EnableDialogueQuitting(RE::StaticFunctionTag*) { DialogueMenuPatch::BlockTab(false); } float ComputeNeededExp(RE::StaticFunctionTag*, std::uint32_t CurrentLevel, float Slope, float Mult, float fExpAcc = 1.0f, float fExpAcc_Level20 = 1.2f, float fExpAcc_Level30 = 1.5f, float fExpAcc_Level40 = 2.0f) { return ComputeNeededExpPoints(CurrentLevel, Slope, Mult, fExpAcc, fExpAcc_Level20, fExpAcc_Level30, fExpAcc_Level40); } bool IsInRegion(RE::StaticFunctionTag*, RE::TESForm* playerRegion) { if (!playerRegion) { return false; } auto* parentCell = RE::PlayerCharacter::GetSingleton()->parentCell; if (!parentCell) { return false; } auto regions = parentCell->GetRegionList(false); if (regions) { for (auto it = regions->begin(); it != regions->end(); it++) { if ((*it) && (*it)->formID == playerRegion->formID) { return true; } } } return false; } uint32_t MoveItemsToCountByKeyword(RE::StaticFunctionTag*, RE::TESObjectREFR* a_sourceRef, RE::TESObjectREFR* a_targetRef, RE::BGSKeyword* a_keyword, uint32_t a_count = 1) { const auto inv = a_sourceRef->GetInventory([&](RE::TESBoundObject& a_exform) { auto miscItem = a_exform.As(); return miscItem && miscItem->HasKeyword(a_keyword); }); uint32_t iResult = 0; for (const auto& item : inv) { if (item.second.first > 0) { int32_t iMoveCount = a_count - GetItemCount(a_targetRef, item.first); if (iMoveCount > 0) { a_sourceRef->RemoveItem(item.first, iMoveCount, RE::ITEM_REMOVE_REASON::kStoreInContainer, nullptr, a_targetRef); iResult += iMoveCount; } } } auto inventory_menu = RE::UI::GetSingleton()->GetMenu(); if (inventory_menu) { inventory_menu->GetRuntimeData().itemList->Update(); } return iResult; } inline void Bind(VM& a_vm) { BIND(CreatePotion); logger::info("{}", "Registered CreatePotion"sv); BIND(GetNewGameCount); logger::info("{}", "Registered GetNewGameCount"sv); BIND(GetCurrentContainer); logger::info("{}", "Registered GetCurrentContainer"sv); BIND(GetPlayerFollowers); logger::info("{}", "Registered GetPlayerFollowers"sv); BIND(GetAutosaveName); logger::info("{}", "Registered GetAutosaveName"sv); BIND(DisableDialogueQuitting); logger::info("{}", "Registered DisableDialogueQuitting"sv); BIND(EnableDialogueQuitting); logger::info("{}", "Registered EnableDialogueQuitting"sv); BIND(ComputeNeededExp); logger::info("{}", "Registered ComputeNeededExp"sv); BIND(IsInRegion); logger::info("{}", "Registered IsInRegion"sv); BIND(MoveItemsToCountByKeyword); logger::info("{}", "Registered MoveItemsToCountByKeyword"sv); } }