1
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

288 lines
8.0 KiB

#pragma once
namespace Papyrus::ArtifactTracker
{
inline bool is_artifact(RE::TESForm* a_form)
{
switch (a_form->GetFormType()) {
case RE::FormType::Armor:
return a_form->GetPlayable();
case RE::FormType::Weapon:
return a_form->GetPlayable() && a_form->formID != 0x000001F4;
case RE::FormType::Book:
return BookCheck::GetBookList().contains(a_form->formID);
case RE::FormType::Misc:
return MiscCheck::GetMiscList().contains(a_form->formID);
default:
return false;
}
}
inline std::int32_t AddAllFormsToList(RE::StaticFunctionTag*,
RE::BGSListForm* a_targetList,
short a_formType,
RE::BGSListForm* a_storedList,
RE::BGSListForm* a_foundList)
{
const auto formType = static_cast<RE::FormType>(a_formType);
switch (formType) {
case RE::FormType::Book:
for (auto const& item : BookCheck::GetBookList()) {
if (!a_storedList->HasForm(item.second) && !a_foundList->HasForm(item.second)) {
a_targetList->AddForm(item.second);
}
}
break;
case RE::FormType::Misc:
for (auto const& item : MiscCheck::GetMiscList()) {
if (!a_storedList->HasForm(item.second) && !a_foundList->HasForm(item.second)) {
a_targetList->AddForm(item.second);
}
}
break;
case RE::FormType::None:
break;
default:
const auto dataHandler = RE::TESDataHandler::GetSingleton();
if (!dataHandler) {
return a_targetList->forms.size();
}
for (const auto& form : dataHandler->GetFormArray(formType)) {
if (!form || !form->GetPlayable()) {
continue;
}
if (a_storedList->HasForm(form) || a_foundList->HasForm(form)) {
continue;
}
a_targetList->AddForm(form);
}
}
return a_targetList->forms.size();
}
inline std::int32_t AddArtifactsToList(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*,
RE::TESForm* a_refOrList,
RE::BGSListForm* a_targetList,
RE::BGSListForm* a_excludeList = NULL)
{
if (!a_refOrList) {
a_vm->TraceStack("a_refOrList in AddArtifactsToList is None", a_stackID);
return 0;
}
if (!a_targetList) {
a_vm->TraceStack("a_targetList in AddArtifactsToList is None", a_stackID);
return 0;
}
if (a_refOrList->Is(RE::FormType::FormList)) {
a_refOrList->As<RE::BGSListForm>()->ForEachForm([&](RE::TESForm& a_exform) {
const auto refrItem = a_exform.As<RE::TESObjectREFR>();
if (refrItem) {
AddArtifactsToList(a_vm, a_stackID, nullptr, refrItem, a_targetList, a_excludeList);
}
return true;
});
return a_targetList->forms.size();
}
const auto containerRef = a_refOrList->As<RE::TESObjectREFR>();
if (!containerRef) {
a_vm->TraceStack("containerRef in AddArtifactsToList is not a reference", a_stackID);
return 0;
}
const auto inv = containerRef->GetInventory([&](RE::TESBoundObject& a_exform) {
return is_artifact(&a_exform) && (!a_excludeList || !a_excludeList->HasForm(&a_exform));
});
for (const auto& item : inv) {
if (item.second.first > 0) {
a_targetList->AddForm(item.first);
}
}
return a_targetList->forms.size();
}
inline RE::TESObjectREFR* GetCellStorage(RE::StaticFunctionTag*,
RE::TESObjectREFR* a_ref,
RE::BGSListForm* a_refList,
RE::TESBoundObject* a_objectToCreate,
bool a_autoCreate = true)
{
RE::TESObjectREFR* result = NULL;
if (!a_ref || !a_refList || !a_objectToCreate) {
return result;
}
RE::TESObjectCELL* cell = a_ref->GetParentCell();
a_refList->ForEachForm([&result, &cell, &a_objectToCreate](RE::TESForm& a_exform) {
const auto ref = a_exform.As<RE::TESObjectREFR>();
if (ref && ref->GetParentCell() == cell && ref->GetBaseObject()->formID == a_objectToCreate->formID) {
result = ref;
return false;
}
return true;
});
if (!result && a_autoCreate) {
result = a_ref->PlaceObjectAtMe(a_objectToCreate, true).get();
result->Disable();
a_refList->AddForm(result);
}
return result;
}
inline void SyncCellStorage(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*,
RE::TESObjectREFR* a_cellStorage,
RE::BGSListForm* a_excludeContainers)
{
if (!a_cellStorage) {
return;
}
std::unordered_map<RE::FormID, bool> cellItems;
const auto cell = a_cellStorage->GetParentCell();
const auto inv = a_cellStorage->GetInventory();
for (const RE::NiPointer<RE::TESObjectREFR> a_ref : cell->references) {
const auto baseObj = a_ref->GetBaseObject();
if (baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref->IsDisabled() && baseObj->As<RE::TESNPC>()->GetRace()->formID == 0x0010760A)) {
if (a_excludeContainers->HasForm(a_ref->formID) || baseObj->formID == 0xDC9E7) { // skip persistent and PlayerBookShelfContainer
continue;
}
const auto contInv = a_ref->GetInventory([&](RE::TESBoundObject& a_object) -> bool {
return !cellItems.contains(a_object.formID);
});
for (const auto& [item, data] : contInv) {
if (data.first > 0) {
cellItems[item->formID] = true;
if (inv.find(item) == inv.end()) {
if (is_artifact(item)) {
a_cellStorage->AddObjectToContainer(item, nullptr, 1, nullptr);
}
}
}
}
continue;
}
if (a_ref->IsDisabled() || a_ref->IsMarkedForDeletion()) {
continue;
}
if (cellItems.contains(baseObj->formID)) {
continue;
}
cellItems[baseObj->formID] = true;
if (!is_artifact(baseObj)) {
continue;
}
if (inv.find(baseObj) == inv.end()) {
a_cellStorage->AddObjectToContainer(baseObj, nullptr, 1, nullptr);
}
}
for (const auto& [item, data] : inv) {
const auto& [count, entry] = data;
if (count > 0 && !cellItems.contains(item->formID)) {
a_cellStorage->RemoveItem(item, count, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);
}
}
cellItems.clear();
}
// From po3's Papyrus Extender
inline std::vector<RE::Actor*> GetPlayerFollowers(RE::StaticFunctionTag*)
{
std::vector<RE::Actor*> result;
if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) {
for (auto& actorHandle : processLists->highActorHandles) {
if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) {
result.push_back(actor.get());
}
}
}
return result;
}
inline std::int32_t AddArtifactsFromFollowersToList(RE::StaticFunctionTag*,
RE::BGSListForm* a_targetList,
RE::BGSListForm* a_excludeList = NULL)
{
for (const auto& actor : GetPlayerFollowers(nullptr)) {
const auto inv = actor->GetInventory([&](RE::TESBoundObject& a_exform) {
return is_artifact(&a_exform) && (!a_excludeList || !a_excludeList->HasForm(&a_exform));
});
for (const auto& item : inv) {
if (item.second.first > 0) {
a_targetList->AddForm(item.first);
}
}
}
return a_targetList->forms.size();
}
inline bool FollowersHaveItem(RE::StaticFunctionTag*,
RE::TESBoundObject* a_form)
{
for (const auto& actor : GetPlayerFollowers(nullptr)) {
const auto inv = actor->GetInventory([&](RE::TESBoundObject& a_object) -> bool {
return a_form->formID == a_object.formID;
});
const auto it = inv.find(a_form);
const auto iCount = it != inv.end() ? it->second.first : 0;
if (iCount > 0) {
return true;
}
}
return false;
}
inline void Bind(VM& a_vm)
{
BIND(AddAllFormsToList);
logger::info("Registered AddAllFormsToList"sv);
BIND(AddArtifactsToList);
logger::info("Registered AddArtifactsToList"sv);
BIND(AddArtifactsFromFollowersToList);
logger::info("Registered AddArtifactsFromFollowersToList"sv);
BIND(GetCellStorage);
logger::info("Registered GetCellStorage"sv);
BIND(SyncCellStorage);
logger::info("Registered SyncCellStorage"sv);
BIND(FollowersHaveItem);
logger::info("Registered FollowersHaveItem"sv);
BIND(GetPlayerFollowers);
logger::info("Registered GetPlayerFollowers"sv);
}
}