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.
279 lines
7.4 KiB
279 lines
7.4 KiB
#pragma once
|
|
|
|
namespace Papyrus::ArtifactTracker
|
|
{
|
|
inline bool is_excluded(RE::TESForm* a_form, RE::TESForm* a_excludeForm = NULL)
|
|
{
|
|
if (!a_excludeForm) {
|
|
return false;
|
|
}
|
|
|
|
const RE::BGSListForm* list = a_excludeForm->As<RE::BGSListForm>();
|
|
|
|
if (list) {
|
|
if (list->HasForm(a_form)) {
|
|
return true;
|
|
} else {
|
|
bool isExcluded = false;
|
|
|
|
list->ForEachForm([&](RE::TESForm& a_exform) {
|
|
const auto exlist = a_exform.As<RE::BGSListForm>();
|
|
if (exlist) {
|
|
if (exlist->forms.size() > 0) {
|
|
if (exlist->forms[0]->Is(RE::FormType::Keyword) ? a_form->HasKeywordInList(exlist, false) : exlist->HasForm(a_form)) {
|
|
isExcluded = true;
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
const auto exkeyword = a_exform.As<RE::BGSKeyword>();
|
|
if (exkeyword && a_form->As<RE::BGSKeywordForm>()->HasKeyword(exkeyword)) {
|
|
isExcluded = true;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
|
|
return isExcluded;
|
|
}
|
|
}
|
|
|
|
const RE::BGSKeyword* keyword = a_excludeForm->As<RE::BGSKeyword>();
|
|
|
|
if (keyword) {
|
|
if (a_form->As<RE::BGSKeywordForm>()->HasKeyword(keyword)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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::TESForm* a_excludeForm = NULL)
|
|
{
|
|
const auto formType = static_cast<RE::FormType>(a_formType);
|
|
|
|
switch (formType) {
|
|
|
|
case RE::FormType::Book:
|
|
for (auto const& item : BookCheck::GetBookList()) {
|
|
if (!a_excludeForm || !is_excluded(item.second, a_excludeForm)) {
|
|
a_targetList->AddForm(item.second);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RE::FormType::Misc:
|
|
for (auto const& item : MiscCheck::GetMiscList()) {
|
|
if (!a_excludeForm || !is_excluded(item.second, a_excludeForm)) {
|
|
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_excludeForm && is_excluded(form, a_excludeForm)) {
|
|
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::TESForm* a_excludeForm = 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, {}, refrItem, a_targetList, a_excludeForm);
|
|
}
|
|
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;
|
|
}
|
|
|
|
auto inv = containerRef->GetInventory([&](RE::TESBoundObject& a_exform) {
|
|
return is_artifact(&a_exform) && !is_excluded(&a_exform, a_excludeForm);
|
|
});
|
|
|
|
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::TESForm* a_excludeForm = NULL)
|
|
{
|
|
if (!a_cellStorage) {
|
|
return;
|
|
}
|
|
|
|
std::unordered_map<RE::FormID, bool> cellItems;
|
|
|
|
const auto storageBase = a_cellStorage->GetBaseObject();
|
|
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_ref->GetBaseObject()->formID == storageBase->formID) {
|
|
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) && !is_excluded(item, a_excludeForm)) {
|
|
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()) {
|
|
if (!a_excludeForm || !is_excluded(baseObj, a_excludeForm)) {
|
|
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();
|
|
}
|
|
|
|
inline void Bind(VM& a_vm)
|
|
{
|
|
BIND(AddAllFormsToList);
|
|
logger::info("Registered AddAllFormsToList"sv);
|
|
BIND(AddArtifactsToList);
|
|
logger::info("Registered AddArtifactsToList"sv);
|
|
BIND(GetCellStorage);
|
|
logger::info("Registered GetCellStorage"sv);
|
|
BIND(SyncCellStorage);
|
|
logger::info("Registered SyncCellStorage"sv);
|
|
}
|
|
}
|
|
|