Add keyword strings to merchandise
This commit is contained in:
parent
ccaa413137
commit
c576a2b885
@ -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;
|
||||||
|
@ -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!");
|
||||||
|
Loading…
Reference in New Issue
Block a user