Semi-working refactor of merch for multiple shelves

Code is now capable of loading merch onto multiple store shelves during load shop.

Still need to fix refresh, loading merch after creating merch and making transactions, the shelf buttons, as well as saving the state of the shelves to the server.
This commit is contained in:
Tyler Hallada 2020-11-28 23:44:48 -05:00
parent e673ac4642
commit d1849735b2
8 changed files with 649 additions and 614 deletions

View File

@ -76,6 +76,7 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnCreateInteriorRefListFail"));
// TODO: may need to dynamically pass shop cell into this function
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
logger::info(FMT_STRING("CreateInteriorRefListImpl lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
if (!cell) {
@ -92,8 +93,13 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry) {
RE::TESObjectREFR * ref = (*entry).get();
const char * name = ref->GetName();
logger::info(FMT_STRING("CreateInteriorRefList ref: {}"), name);
const RE::TESBoundObject * base = ref->GetBaseObject();
RE::FormID ref_form_id = ref->GetFormID();
logger::info(FMT_STRING("CreateInteriorRefList ref: {}, form_id: {:x}"), name, (uint32_t)ref_form_id);
if (ref->IsDisabled()) {
logger::info("CreateInteriorRefList skipping ref since it is disabled");
continue;
}
RE::TESBoundObject * base = ref->GetBaseObject();
if (base) {
RE::FormID base_form_id = base->GetFormID();
const RE::FormType form_type = base->GetFormType();
@ -113,11 +119,9 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
logger::info(FMT_STRING("CreateInteriorRefList position: {:.2f}, {:.2f}, {:.2f} angle: {:.2f}, {:.2f}, {:.2f} scale: {:d}"), position_x, position_y, position_z, angle_x, angle_y, angle_z, scale);
logger::info(FMT_STRING("CreateInteriorRefList deleted: {:d}, wants delete: {:d}"), ref->IsMarkedForDeletion(), ref->inGameFormFlags.all(RE::TESObjectREFR::InGameFormFlag::kWantsDelete));
RE::TESFile * base_file = base->GetFile(0);
char * base_file_name = base_file->fileName;
bool is_light = base_file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
uint32_t base_local_form_id = is_light ? base_form_id & 0xfff : base_form_id & 0xFFFFFF;
RE::FormID ref_form_id = ref->GetFormID();
std::pair<uint32_t, const char*> id_parts = get_local_form_id_and_mod_name(base);
uint32_t base_local_form_id = id_parts.first;
const char * base_file_name = id_parts.second;
uint16_t ref_mod_index = ref_form_id >> 24;
char * ref_file_name = nullptr;
uint32_t ref_local_form_id = ref_form_id & 0xFFFFFF;
@ -133,7 +137,7 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
}
}
logger::info(FMT_STRING("CreateInteriorRefList ref_file_name: {}, base_file_name: {}"), ref_file_name, base_file_name);
if (strcmp("Bazaar Realm.esp", base_file_name) == 0) {
if (strcmp(base_file_name, MOD_NAME) == 0) {
logger::info(FMT_STRING("CreateInteriorRefList ref base is in Bazaar Ream.esp: {:x}"), base_local_form_id);
if (ignored_shelf_related_form_ids.find(base_local_form_id) != ignored_shelf_related_form_ids.end()) {
logger::info("CreateInteriorRefList ref is an ignored shelf related form");
@ -218,8 +222,8 @@ bool CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE
bool ClearCell(RE::StaticFunctionTag*) {
logger::info("Entered ClearCell");
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
using func_t = bool(RE::TESObjectREFR* a_thisObj, void* a_param1, void* a_param2, double& a_result);
// TODO: the cell will need to be dynamically passed in
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
logger::info(FMT_STRING("ClearCell lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
if (!cell) {
@ -235,18 +239,7 @@ bool ClearCell(RE::StaticFunctionTag*) {
int mod_index = form_id >> 24;
if (mod_index != 255) {
RE::TESFile * file = ref->GetDescriptionOwnerFile();
if (file) {
bool is_light = file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
uint32_t local_form_id = is_light ? ref->GetFormID() & 0xfff : form_id & 0xFFFFFF;
if (!data_handler->LookupForm<RE::TESObjectREFR>(local_form_id, file->fileName)) {
logger::info(FMT_STRING("ClearCell ref was not in mod file! {:x} {}"), local_form_id, ref->GetName());
} else {
logger::info(FMT_STRING("ClearCell ref in mod file {:x} {}"), local_form_id, ref->GetName());
}
} else {
logger::info(FMT_STRING("ClearCell ref not in ANY file! {:x} {}"), (uint32_t)form_id, ref->GetName());
}
// TODO: recognize somehow that this ref was a pre-placed initially disabled furnature upgrade piece and disable it now if so
++entry;
} else {
logger::info(FMT_STRING("ClearCell ref is a temp ref, deleting {:x} {}"), (uint32_t)form_id, ref->GetName());
@ -267,6 +260,14 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
return;
}
// Testing to see what ExtraLinkedRefChildren stores
RE::ExtraLinkedRefChildren* linkedChildren = (RE::ExtraLinkedRefChildren*)public_chest->extraList.GetByType(RE::ExtraDataType::kLinkedRefChildren);
if (linkedChildren) {
logger::info(FMT_STRING("CreateMerchandiseList public_chest has linkedChildren: size: {}"), linkedChildren->linkedChildren.size());
} else {
logger::info("CreateMerchandiseList public_chest has no linkedChildren");
}
// Placing the refs must be done on the main thread otherwise calling MoveTo causes a crash
auto task = SKSE::GetTaskInterface();
task->AddTask([result, target_ref, private_chest, public_chest, quest]() {
@ -277,6 +278,7 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
using func_t2 = decltype(&MoveTo);
REL::Relocation<func_t2> MoveTo_Native(RE::Offset::TESObjectREFR::MoveTo);
REL::ID extra_linked_ref_vtbl(static_cast<std::uint64_t>(229564));
std::vector<RE::TESObjectREFR*> shelves;
RE::BGSKeyword* shelf_keyword = data_handler->LookupForm<RE::BGSKeyword>(KEYWORD_SHELF, MOD_NAME);
RE::BGSKeyword* chest_keyword = data_handler->LookupForm<RE::BGSKeyword>(KEYWORD_CHEST, MOD_NAME);
@ -285,7 +287,7 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
RE::BGSKeyword* next_keyword = data_handler->LookupForm<RE::BGSKeyword>(KEYWORD_NEXT, MOD_NAME);
RE::BGSKeyword* prev_keyword = data_handler->LookupForm<RE::BGSKeyword>(KEYWORD_PREV, MOD_NAME);
SKSE::RegistrationMap<bool> successReg = SKSE::RegistrationMap<bool>();
SKSE::RegistrationMap<bool, std::vector<RE::TESObjectREFR*>> successReg = SKSE::RegistrationMap<bool, std::vector<RE::TESObjectREFR*>>();
successReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListFail"));
@ -344,6 +346,10 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
game_ref = data_handler->LookupForm<RE::TESObjectREFR>(ref.ref_local_form_id, ref.ref_mod_name);
if (game_ref) {
logger::info(FMT_STRING("LoadInteriorRefList lookup ref name: {}, form_id: {:x}"), game_ref->GetName(), (uint32_t)game_ref->GetFormID());
if (game_ref->IsDisabled()) {
logger::info("LoadInteriorRefList lookup ref is disabled, enabling");
game_ref->formFlags &= ~RE::TESObjectREFR::RecordFlags::kInitiallyDisabled;
}
} else {
logger::info(FMT_STRING("LoadInteriorRefList lookup ref not found, ref_mod_name: {}, ref_local_form_id: {:x}"), ref.ref_mod_name, ref.ref_local_form_id);
}
@ -392,7 +398,8 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
RE::TESObjectREFR* shelf_ref = PlaceAtMe_Native(a_vm, 0, target_ref, form, 1, false, false);
MoveTo_Native(shelf_ref, shelf_ref->CreateRefHandle(), cell, cell->worldSpace, position, angle);
shelf_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
RE::ExtraLinkedRef* shelf_extra_linked_ref = (RE::ExtraLinkedRef*)RE::BSExtraData::Create(sizeof(RE::ExtraLinkedRef), extra_linked_ref_vtbl.address());
shelves.push_back(shelf_ref);
RE::ExtraLinkedRef* shelf_extra_linked_ref = RE::BSExtraData::Create<RE::ExtraLinkedRef>(extra_linked_ref_vtbl.address());
shelf_extra_linked_ref->linkedRefs.push_back({public_chest_keyword, public_chest});
shelf_extra_linked_ref->linkedRefs.push_back({chest_keyword, private_chest});
shelf_ref->extraList.Add(shelf_extra_linked_ref);
@ -429,7 +436,7 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
button_ref->data.angle = button_angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
button_ref->refScale = button.position.scale;
RE::ExtraLinkedRef* button_extra_linked_ref = (RE::ExtraLinkedRef*)RE::BSExtraData::Create(sizeof(RE::ExtraLinkedRef), extra_linked_ref_vtbl.address());
RE::ExtraLinkedRef* button_extra_linked_ref = RE::BSExtraData::Create<RE::ExtraLinkedRef>(extra_linked_ref_vtbl.address());
button_extra_linked_ref->linkedRefs.push_back({shelf_keyword, shelf_ref});
button_ref->extraList.Add(button_extra_linked_ref);
@ -459,7 +466,7 @@ void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* targe
return;
}
successReg.SendEvent(true);
successReg.SendEvent(true, shelves);
successReg.Unregister(quest);
failReg.Unregister(quest);
});

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +1,44 @@
#pragma once
bool ToggleMerchandise(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
int32_t shop_id,
RE::TESObjectREFR* merchant_shelf,
RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword
);
bool LoadNextMerchandise(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
int32_t shop_id,
RE::TESObjectREFR* merchant_shelf,
RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword
);
bool LoadPrevMerchandise(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
int32_t shop_id,
RE::TESObjectREFR* merchant_shelf,
RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword
);
//bool ToggleMerchandise(
// RE::StaticFunctionTag*,
// RE::BSFixedString api_url,
// RE::BSFixedString api_key,
// int32_t shop_id,
// RE::TESObjectREFR* merchant_shelf
//);
//bool LoadNextMerchandise(
// RE::StaticFunctionTag*,
// RE::BSFixedString api_url,
// RE::BSFixedString api_key,
// int32_t shop_id,
// RE::TESObjectREFR* merchant_shelf
//);
//bool LoadPrevMerchandise(
// RE::StaticFunctionTag*,
// RE::BSFixedString api_url,
// RE::BSFixedString api_key,
// int32_t shop_id,
// RE::TESObjectREFR* merchant_shelf
//);
bool LoadMerchandiseByShopId(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
int32_t shop_id,
RE::TESObjectREFR* merchant_shelf,
RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword
RE::TESObjectCELL* cell,
std::vector<RE::TESObjectREFR*> merchant_shelves,
RE::TESObjectREFR* merchant_chest
);
bool RefreshMerchandise(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
int32_t shop_id,
RE::TESObjectREFR* merchant_shelf,
RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword
);
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* activator_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword);
//bool RefreshMerchandise(
// RE::StaticFunctionTag*,
// RE::BSFixedString api_url,
// RE::BSFixedString api_key,
// int32_t shop_id,
// RE::TESObjectREFR* merchant_shelf
//);
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf);
bool ReplaceAllMerch3D(RE::StaticFunctionTag*, RE::TESObjectCELL* cell);
bool CreateMerchandiseList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* merchant_chest);
int GetMerchandiseQuantity(RE::StaticFunctionTag*, RE::TESObjectREFR* activator);
int GetMerchandisePrice(RE::StaticFunctionTag*, RE::TESObjectREFR* activator);

View File

@ -1,4 +1,5 @@
#include "bindings.h"
#include "utils.h"
void CreateTransactionImpl(
RE::BSFixedString api_url,
@ -33,10 +34,9 @@ void CreateTransactionImpl(
RE::FormID form_id = merch_base->GetFormID();
logger::info(FMT_STRING("CreateTransactionImpl merch_base form_id: {:x}, name: {}, type: {:x}"), (uint32_t)form_id, name, (uint32_t)form_type);
RE::TESFile * file = merch_base->GetFile(0);
const char * mod_name = file->fileName;
bool is_light = file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
uint32_t local_form_id = is_light ? form_id & 0xfff : form_id & 0xFFFFFF;
std::pair<uint32_t, const char*> id_parts = get_local_form_id_and_mod_name(merch_base);
uint32_t local_form_id = id_parts.first;
const char* mod_name = id_parts.second;
logger::info(FMT_STRING("CreateTransactionImpl merch_base form file_name: {}, local_form_id: {:x}"), mod_name, local_form_id);
// TODO: implement is_food

View File

@ -1,10 +1,12 @@
#pragma once
const char* MOD_NAME = "Bazaar Realm.esp";
const uint32_t KEYWORD_SHELF = 0x002fb1;
const uint32_t KEYWORD_CHEST = 0x002fb2;
const uint32_t KEYWORD_PUBLIC_CHEST = 0x00603e;
const uint32_t KEYWORD_TOGGLE = 0x00351b;
const uint32_t KEYWORD_NEXT = 0x00351c;
const uint32_t KEYWORD_PREV = 0x00351d;
const uint32_t KEYWORD_ITEM = 0x003517;
const uint32_t KEYWORD_ACTIVATOR = 0x004AA8;
constexpr const char* MOD_NAME = "Bazaar Realm.esp";
constexpr uint32_t KEYWORD_SHELF = 0x002fb1;
constexpr uint32_t KEYWORD_CHEST = 0x002fb2;
constexpr uint32_t KEYWORD_PUBLIC_CHEST = 0x00603e;
constexpr uint32_t KEYWORD_TOGGLE = 0x00351b;
constexpr uint32_t KEYWORD_NEXT = 0x00351c;
constexpr uint32_t KEYWORD_PREV = 0x00351d;
constexpr uint32_t KEYWORD_ITEM = 0x003517;
constexpr uint32_t KEYWORD_ACTIVATOR = 0x004AA8;
constexpr uint32_t ACTIVATOR_STATIC = 0x002a3b;

View File

@ -23,12 +23,13 @@ bool RegisterFuncs(RE::BSScript::IVirtualMachine* a_vm)
a_vm->RegisterFunction("ClearCell", "BRInteriorRefList", ClearCell);
a_vm->RegisterFunction("Load", "BRInteriorRefList", LoadInteriorRefList);
a_vm->RegisterFunction("LoadByShopId", "BRInteriorRefList", LoadInteriorRefListByShopId);
a_vm->RegisterFunction("Toggle", "BRMerchandiseList", ToggleMerchandise);
a_vm->RegisterFunction("NextPage", "BRMerchandiseList", LoadNextMerchandise);
a_vm->RegisterFunction("PrevPage", "BRMerchandiseList", LoadPrevMerchandise);
//a_vm->RegisterFunction("Toggle", "BRMerchandiseList", ToggleMerchandise);
//a_vm->RegisterFunction("NextPage", "BRMerchandiseList", LoadNextMerchandise);
//a_vm->RegisterFunction("PrevPage", "BRMerchandiseList", LoadPrevMerchandise);
a_vm->RegisterFunction("Load", "BRMerchandiseList", LoadMerchandiseByShopId);
a_vm->RegisterFunction("Refresh", "BRMerchandiseList", RefreshMerchandise);
//a_vm->RegisterFunction("Refresh", "BRMerchandiseList", RefreshMerchandise);
a_vm->RegisterFunction("Replace3D", "BRMerchandiseList", ReplaceMerch3D);
a_vm->RegisterFunction("ReplaceAll3D", "BRMerchandiseList", ReplaceAllMerch3D);
a_vm->RegisterFunction("Create", "BRMerchandiseList", CreateMerchandiseList);
a_vm->RegisterFunction("GetQuantity", "BRMerchandiseList", GetMerchandiseQuantity);
a_vm->RegisterFunction("GetPrice", "BRMerchandiseList", GetMerchandisePrice);

View File

@ -19,3 +19,11 @@ RE::NiPoint3 rotate_point(RE::NiPoint3 point, RE::NiMatrix3 rotation_matrix) {
(point.x * rotation_matrix.entry[0][2]) + (point.y * rotation_matrix.entry[1][2]) + (point.z * rotation_matrix.entry[2][2])
);
}
std::pair<uint32_t, const char*> get_local_form_id_and_mod_name(RE::TESForm* form) {
RE::FormID form_id = form->GetFormID();
RE::TESFile* file = form->GetFile(0);
const char* mod_name = file->fileName;
bool is_light = file->recordFlags.all(RE::TESFile::RecordFlag::kSmallFile);
return std::pair(is_light ? form_id & 0xfff : form_id & 0xFFFFFF, mod_name);
}

View File

@ -1,3 +1,4 @@
#pragma once
RE::NiMatrix3 get_rotation_matrix(RE::NiPoint3 rotation);
RE::NiPoint3 rotate_point(RE::NiPoint3 point, RE::NiMatrix3 rotation_matrix);
std::pair<uint32_t, const char*> get_local_form_id_and_mod_name(RE::TESForm* form);