|
|
|
@ -18,15 +18,18 @@ namespace ArtifactTracker |
|
|
|
|
std::unordered_map<RE::FormID, RE::TESObjectREFR*> g_persistentMap; |
|
|
|
|
RE::TESObjectREFR* g_cellStorage; |
|
|
|
|
|
|
|
|
|
void Init() |
|
|
|
|
bool Init() |
|
|
|
|
{ |
|
|
|
|
g_bLoaded = false; |
|
|
|
|
if (g_bLoaded) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const auto dataHandler = RE::TESDataHandler::GetSingleton(); |
|
|
|
|
|
|
|
|
|
if (!dataHandler) { |
|
|
|
|
SKSE::log::error("Failed to call RE::TESDataHandler::GetSingleton()"); |
|
|
|
|
return; |
|
|
|
|
SKSE::log::error("Unable to call RE::TESDataHandler::GetSingleton()"); |
|
|
|
|
RE::DebugMessageBox("Unable to initialize Artifact Tracker."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
g_cellContainer = dataHandler->LookupForm(0x800, "Artifact Tracker.esp")->As<RE::TESBoundObject>(); // ETR_CellStorageContainer
|
|
|
|
@ -40,111 +43,95 @@ namespace ArtifactTracker |
|
|
|
|
|
|
|
|
|
const auto recipeKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe
|
|
|
|
|
const auto excludeKeywords = dataHandler->LookupForm<RE::BGSListForm>(0x801, "Artifact Tracker.esp"); // ETR_ExcludeMiscKeywords
|
|
|
|
|
const auto dummyKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xF3E6C, "Skyrim.esm"); // Dummy
|
|
|
|
|
const auto extraArtifactKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xDE3FD3, "Update.esm"); // ETR_ExtraArtifact
|
|
|
|
|
const auto notArtifactKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xDE3FD4, "Update.esm"); // ETR_NotArtifact
|
|
|
|
|
|
|
|
|
|
if (!g_cellContainer || !g_listNew || !g_listStored || !g_listFound || !g_persistentStorage || !g_homeKeyword || !recipeKeyword || !excludeKeywords) { |
|
|
|
|
SKSE::log::warn("Failed to load data from Artifact Tracker.esp"); |
|
|
|
|
RE::DebugMessageBox("Failed to load data from Artifact Tracker.esp, the mod is disabled."); |
|
|
|
|
return; |
|
|
|
|
if (!g_cellContainer || !g_listNew || !g_listStored || !g_listFound || !g_persistentStorage || !g_homeKeyword || !recipeKeyword || !excludeKeywords || !dummyKeyword || !extraArtifactKeyword || !notArtifactKeyword) { |
|
|
|
|
SKSE::log::warn("Unable to load data from Artifact Tracker.esp"); |
|
|
|
|
RE::DebugMessageBox("Unable to load data from Artifact Tracker.esp, the mod is disabled."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Preloading item lists
|
|
|
|
|
|
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Weapon); |
|
|
|
|
for (const auto& form : dataHandler->GetFormArray(RE::FormType::Weapon)) { |
|
|
|
|
if (form->GetPlayable()) { |
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectWEAP>()) { |
|
|
|
|
if (form->GetPlayable() && !form->IsBound() && !form->weaponData.flags.all(RE::TESObjectWEAP::Data::Flag::kCantDrop) && !form->IsDeleted() && !form->HasKeyword(dummyKeyword) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
g_artifactMap.erase(0x1F4); // Unarmed
|
|
|
|
|
|
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Armor); |
|
|
|
|
for (const auto& form : dataHandler->GetFormArray(RE::FormType::Armor)) { |
|
|
|
|
if (form->GetPlayable()) { |
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectARMO>()) { |
|
|
|
|
if (form->GetPlayable() && !form->IsDeleted() && !form->HasKeyword(dummyKeyword) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
g_artifactMap.erase(0xD64); // SkinNaked
|
|
|
|
|
g_artifactMap.erase(0x69CE3); // SkinNakedBeast
|
|
|
|
|
g_artifactMap.erase(0xCDD86); // SkinNakedWerewolfBeast
|
|
|
|
|
|
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Book); |
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectBOOK>()) { |
|
|
|
|
if (form && !form->TeachesSpell() && (form->HasKeyword(recipeKeyword) || BookCheck::IsBook(form))) { |
|
|
|
|
if (!form->TeachesSpell() && !form->IsDeleted() && (form->HasKeyword(recipeKeyword) || form->HasKeyword(extraArtifactKeyword) || BookCheck::IsBook(form)) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Misc); |
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectMISC>()) { |
|
|
|
|
if (form->GetPlayable() && !form->HasKeywordInList(excludeKeywords, false)) { |
|
|
|
|
if (form->GetPlayable() && !form->IsDeleted() && (form->HasKeyword(extraArtifactKeyword) || !form->HasKeywordInList(excludeKeywords, false)) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
g_artifactMap.erase(0xA); // Lockpick
|
|
|
|
|
g_artifactMap.erase(0xF); // Gold
|
|
|
|
|
|
|
|
|
|
// Fishing CC
|
|
|
|
|
const auto plaqueFish = dataHandler->LookupForm<RE::BGSListForm>(0xF4B, "ccBGSSSE001-Fish.esm"); // ccBGSSSE001_FishPlaqueGiftFilterList
|
|
|
|
|
if (plaqueFish) { |
|
|
|
|
plaqueFish->ForEachForm([&](RE::TESForm& a_form) { |
|
|
|
|
g_artifactMap[a_form.formID] = &a_form; |
|
|
|
|
return true; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
OnGameLoad(); |
|
|
|
|
EventListener::Install(); |
|
|
|
|
|
|
|
|
|
g_bLoaded = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void OnKeywordDistribution() |
|
|
|
|
{ |
|
|
|
|
const auto dataHandler = RE::TESDataHandler::GetSingleton(); |
|
|
|
|
const auto extraArtifactKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xDE3FD3, "Update.esm"); // ETR_ExtraArtifact
|
|
|
|
|
|
|
|
|
|
if (!dataHandler || !extraArtifactKeyword) { |
|
|
|
|
SKSE::log::error("Unable to load ETR_ExtraArtifact in OnKeywordDistribution"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectBOOK>()) { |
|
|
|
|
if (form->HasKeyword(extraArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectMISC>()) { |
|
|
|
|
if (form->GetPlayable() && form->HasKeyword(extraArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::AlchemyItem>()) { |
|
|
|
|
if (form->HasKeyword(extraArtifactKeyword)) { |
|
|
|
|
if (!form->IsDeleted() && form->HasKeyword(extraArtifactKeyword) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::AlchemyItem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::IngredientItem>()) { |
|
|
|
|
if (form->HasKeyword(extraArtifactKeyword)) { |
|
|
|
|
if (!form->IsDeleted() && form->HasKeyword(extraArtifactKeyword) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Ingredient); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESSoulGem>()) { |
|
|
|
|
if (form->HasKeyword(extraArtifactKeyword)) { |
|
|
|
|
if (!form->IsDeleted() && form->HasKeyword(extraArtifactKeyword) && !form->HasKeyword(notArtifactKeyword)) { |
|
|
|
|
g_artifactMap[form->formID] = form; |
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::SoulGem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fishing CC (remove, when KID adds formlist support)
|
|
|
|
|
const auto plaqueFish = dataHandler->LookupForm<RE::BGSListForm>(0xF4B, "ccBGSSSE001-Fish.esm"); // ccBGSSSE001_FishPlaqueGiftFilterList
|
|
|
|
|
if (plaqueFish) { |
|
|
|
|
plaqueFish->ForEachForm([&](RE::TESForm& a_form) { |
|
|
|
|
g_artifactMap[a_form.formID] = &a_form; |
|
|
|
|
return true; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
OnGameLoad(); |
|
|
|
|
EventListener::Install(); |
|
|
|
|
|
|
|
|
|
g_bLoaded = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsArtifact(RE::TESForm* a_form) |
|
|
|
|
bool IsArtifact(const RE::TESForm* a_form) |
|
|
|
|
{ |
|
|
|
|
return a_form && g_artifactFormTypes.contains(a_form->GetFormType()) && g_artifactMap.contains(a_form->formID); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RE::TESForm* GetArtifactByID(RE::FormID a_formID) |
|
|
|
|
RE::TESForm* GetArtifactByID(const RE::FormID a_formID) |
|
|
|
|
{ |
|
|
|
|
if (!a_formID) { |
|
|
|
|
return nullptr; |
|
|
|
@ -169,7 +156,7 @@ namespace ArtifactTracker |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SetContainerMode(bool bOpening) |
|
|
|
|
void SetContainerMode(const bool bOpening) |
|
|
|
|
{ |
|
|
|
|
if (bOpening) { |
|
|
|
|
|
|
|
|
@ -229,7 +216,7 @@ namespace ArtifactTracker |
|
|
|
|
return baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref->IsDisabled() && baseObj->As<RE::TESNPC>()->GetRace()->formID == 0x0010760A); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void OnCellEnter(RE::FormID a_formID) |
|
|
|
|
void OnCellEnter(const RE::FormID a_formID) |
|
|
|
|
{ |
|
|
|
|
RE::TESObjectCELL* cell = RE::TESForm::LookupByID<RE::TESObjectCELL>(a_formID); |
|
|
|
|
RE::BGSLocation* location = cell ? cell->GetLocation() : nullptr; |
|
|
|
@ -260,7 +247,7 @@ namespace ArtifactTracker |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void OnCellEnter(RE::BGSLocation* location, RE::TESObjectCELL* cell) |
|
|
|
|
void OnCellEnter(const RE::BGSLocation* location, const RE::TESObjectCELL* cell) |
|
|
|
|
{ |
|
|
|
|
if (!location || !cell->IsInteriorCell() || cell != RE::PlayerCharacter::GetSingleton()->GetParentCell() || !location->HasKeyword(g_homeKeyword)) { |
|
|
|
|
ToggleHomeMode(nullptr); |
|
|
|
@ -303,7 +290,7 @@ namespace ArtifactTracker |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SyncCellStorage(RE::FormID a_ignoreRef) |
|
|
|
|
void SyncCellStorage(const RE::TESObjectREFR* a_ignoreRef) |
|
|
|
|
{ |
|
|
|
|
if (!IsHome()) { |
|
|
|
|
#ifdef _DEBUG |
|
|
|
@ -322,7 +309,7 @@ namespace ArtifactTracker |
|
|
|
|
const auto inv = g_cellStorage->GetInventory(); |
|
|
|
|
|
|
|
|
|
for (const auto& a_ref : cell->references) { |
|
|
|
|
if (a_ignoreRef == a_ref->formID) { |
|
|
|
|
if (a_ignoreRef && a_ignoreRef->formID == a_ref->formID) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -428,7 +415,7 @@ namespace ArtifactTracker |
|
|
|
|
if (!g_bHomeContainer) { |
|
|
|
|
const auto container = RE::TESForm::LookupByID<RE::TESObjectREFR>(a_event->oldContainer); |
|
|
|
|
if (container && !GetItemCount(container, form)) { |
|
|
|
|
SyncCellStorage(container->formID); |
|
|
|
|
SyncCellStorage(container); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -575,7 +562,7 @@ namespace ArtifactTracker |
|
|
|
|
if (g_cellStorage && IsInSameCell(container)) { // deleted from container at home
|
|
|
|
|
|
|
|
|
|
if (!GetItemCount(container, form)) { |
|
|
|
|
SyncCellStorage(container->formID); |
|
|
|
|
SyncCellStorage(container); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|