Add keyword strings to merchandise

This commit is contained in:
Tyler Hallada 2021-02-09 00:59:06 -05:00
parent ccaa413137
commit c576a2b885
2 changed files with 134 additions and 5 deletions

View File

@ -5,8 +5,8 @@
#include "FormIds.h" #include "FormIds.h"
const float PI = 3.14159265358979f; const float PI = 3.14159265358979f;
const std::map<uint32_t, uint32_t> shelf_types { {0x00603f, 1} }; const std::unordered_map<uint32_t, uint32_t> shelf_types { {0x00603f, 1} };
const std::map<uint32_t, uint32_t> shelf_form_ids { {1, 0x00603f} }; const std::unordered_map<uint32_t, uint32_t> shelf_form_ids { {1, 0x00603f} };
struct Position { struct Position {
float position_x; float position_x;
float position_y; float position_y;
@ -21,7 +21,7 @@ struct Button {
Position position; Position position;
}; };
enum ButtonType { Toggle, Next, Previous }; enum ButtonType { Toggle, Next, Previous };
const std::map<uint32_t, std::map<ButtonType, Button>> shelf_buttons { const std::unordered_map<uint32_t, std::unordered_map<ButtonType, Button>> shelf_buttons {
{ 1, { { 1, {
{ ButtonType::Next, Button { { ButtonType::Next, Button {
0x002fab, 0x002fab,
@ -412,7 +412,7 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
auto maybe_buttons = shelf_buttons.find(shelf.shelf_type); auto maybe_buttons = shelf_buttons.find(shelf.shelf_type);
if (maybe_buttons != shelf_buttons.end()) { if (maybe_buttons != shelf_buttons.end()) {
std::map<ButtonType, Button> buttons = (*maybe_buttons).second; std::unordered_map<ButtonType, Button> buttons = (*maybe_buttons).second;
for (auto entry = buttons.begin(); entry != buttons.end(); ++entry) { for (auto entry = buttons.begin(); entry != buttons.end(); ++entry) {
ButtonType button_type = (*entry).first; ButtonType button_type = (*entry).first;
Button button = (*entry).second; Button button = (*entry).second;

View File

@ -855,6 +855,122 @@ bool ReplaceAllMerch3D(RE::StaticFunctionTag*, RE::TESObjectCELL* cell) {
return true; return true;
} }
RE::BGSKeywordForm* LookupKeywordForm(RE::FormID form_id, RE::FormType form_type) {
// Casting directly from container entry TESBoundObject to BGSKeywordForm is not working.
// Instead, lookup form class instance that implements BGSKeywordForm by form id and form type and upcast to BGSKeywordForm
switch (form_type) {
case RE::FormType::AlchemyItem: {
RE::AlchemyItem* item = RE::TESForm::LookupByID<RE::AlchemyItem>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup AlchemyItem with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Ammo: {
RE::TESAmmo* item = RE::TESForm::LookupByID<RE::TESAmmo>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESAmmo with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Armor: {
RE::TESObjectARMO* item = RE::TESForm::LookupByID<RE::TESObjectARMO>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESObjectARMO with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Book: {
RE::TESObjectBOOK* item = RE::TESForm::LookupByID<RE::TESObjectBOOK>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESObjectBOOK with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Ingredient: {
RE::IngredientItem* item = RE::TESForm::LookupByID<RE::IngredientItem>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup IngredientItem with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::KeyMaster: {
RE::TESKey* item = RE::TESForm::LookupByID<RE::TESKey>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESKey with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Misc: {
RE::TESObjectMISC* item = RE::TESForm::LookupByID<RE::TESObjectMISC>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESObjectMISC with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Scroll: {
RE::ScrollItem* item = RE::TESForm::LookupByID<RE::ScrollItem>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup ScrollItem with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::SoulGem: {
RE::TESSoulGem* item = RE::TESForm::LookupByID<RE::TESSoulGem>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESSoulGem with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} case RE::FormType::Weapon: {
RE::TESObjectWEAP* item = RE::TESForm::LookupByID<RE::TESObjectWEAP>(form_id);
if (item) {
return dynamic_cast<RE::BGSKeywordForm*>(item);
} else {
logger::warn(FMT_STRING("LookupKeywordForm couldn't lookup TESObjectWEAP with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
break;
} default:
logger::warn(FMT_STRING("LookupKeywordForm form cannot have keywords with form_id: {:x} and form_type: {:x}"), (uint32_t)form_id, (uint32_t)form_type);
}
}
std::vector<const char*> GetKeywords(RE::FormID form_id, RE::FormType form_type) {
std::vector<const char*> keywords;
RE::BGSKeywordForm* keyword_form = LookupKeywordForm(form_id, form_type);
if (!keyword_form) {
logger::warn(FMT_STRING("GetKeywords form {:x} type: {:x} is not a keyword form"), (uint32_t)form_id, (uint32_t)form_type);
return keywords;
}
logger::info(FMT_STRING("GetKeywords GetNumKeywords: {:d}"), keyword_form->GetNumKeywords());
for (int i = 0; i != keyword_form->GetNumKeywords(); i++) {
std::optional<RE::BGSKeyword*> maybe_keyword = keyword_form->GetKeywordAt(i);
logger::info(FMT_STRING("GetKeywords keyword {:d} has_value: {:d}"), i, maybe_keyword.has_value());
if (maybe_keyword.has_value()) {
RE::BGSKeyword* keyword = dynamic_cast<RE::BGSKeyword*>(maybe_keyword.value());
if (keyword != nullptr) {
logger::info(FMT_STRING("GetKeywords keyword formId: {:x} editorFormId: {}"), (uint32_t)keyword->GetFormID(), keyword->GetFormEditorID());
keywords.push_back(keyword->GetFormEditorID());
} else {
logger::warn(FMT_STRING("GetKeywords ignoring invalid keyword {:x}"), (uintptr_t)keyword);
}
}
}
return keywords;
}
void CreateMerchandiseListImpl( void CreateMerchandiseListImpl(
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
@ -866,6 +982,7 @@ void CreateMerchandiseListImpl(
logger::info("Entered CreateMerchandiseListImpl"); logger::info("Entered CreateMerchandiseListImpl");
RE::TESDataHandler* data_handler = RE::TESDataHandler::GetSingleton(); RE::TESDataHandler* data_handler = RE::TESDataHandler::GetSingleton();
std::vector<RawMerchandise> merch_records; std::vector<RawMerchandise> merch_records;
std::vector<std::vector<const char*>> keyword_strings;
if (!merchant_chest) { if (!merchant_chest) {
logger::error("CreateMerchandiseListImpl merchant_chest is null!"); logger::error("CreateMerchandiseListImpl merchant_chest is null!");
@ -903,12 +1020,22 @@ void CreateMerchandiseListImpl(
std::pair<uint32_t, const char*> id_parts = get_local_form_id_and_mod_name(base); std::pair<uint32_t, const char*> id_parts = get_local_form_id_and_mod_name(base);
uint32_t local_form_id = id_parts.first; uint32_t local_form_id = id_parts.first;
const char* mod_name = id_parts.second; const char* mod_name = id_parts.second;
logger::info(FMT_STRING("CreateMerchandiseList base form file_name: {}, local_form_id"), mod_name, local_form_id); logger::info(FMT_STRING("CreateMerchandiseList base form file_name: {}, local_form_id: {:x}"), mod_name, local_form_id);
std::vector<const char*> keywords = GetKeywords(form_id, base->GetFormType());
// TODO: implement is_food // TODO: implement is_food
bool is_food = false; bool is_food = false;
// TODO: allow user to set price // TODO: allow user to set price
uint32_t price = base->GetGoldValue(); uint32_t price = base->GetGoldValue();
const char** keywords_ptr = nullptr;
uintptr_t keywords_len = keywords.size();
if (keywords_len > 0) {
// Pushing to another vector to keep the strings allocated beyond the scope of this loop
keyword_strings.push_back(keywords);
keywords_ptr = &keyword_strings[keyword_strings.size() - 1][0];
}
merch_records.push_back({ merch_records.push_back({
mod_name, mod_name,
@ -918,6 +1045,8 @@ void CreateMerchandiseListImpl(
form_type, form_type,
is_food, is_food,
price, price,
keywords_ptr,
keywords_len,
}); });
} else { } else {
logger::warn("CreateMerchandiseList skipping inventory entry which has no base form!"); logger::warn("CreateMerchandiseList skipping inventory entry which has no base form!");