Save and load shelves from server interior_ref_lists

During CreateInteriorRefList, save shelf positions in a separate vector and ignore their buttons. In Load, recreate the shelves and their buttons from the shelf vec.

Also start storing FormIDs in a header file so I don't have to keep passing Keywords into all the functions from Papyrus.
This commit is contained in:
Tyler Hallada 2020-11-21 01:36:14 -05:00
parent 0f1b017158
commit e673ac4642
8 changed files with 285 additions and 45 deletions

View File

@ -235,6 +235,7 @@
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</MultiProcessorCompilation> <MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</MultiProcessorCompilation>
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</MultiProcessorCompilation> <MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</MultiProcessorCompilation>
</ClCompile> </ClCompile>
<ClCompile Include="src\utils.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="src\BRClient.h" /> <ClInclude Include="src\BRClient.h" />
@ -243,8 +244,10 @@
<ClInclude Include="src\BROwner.h" /> <ClInclude Include="src\BROwner.h" />
<ClInclude Include="src\BRShop.h" /> <ClInclude Include="src\BRShop.h" />
<ClInclude Include="src\BRTransaction.h" /> <ClInclude Include="src\BRTransaction.h" />
<ClInclude Include="src\FormIds.h" />
<ClInclude Include="src\NativeFunctions.h" /> <ClInclude Include="src\NativeFunctions.h" />
<ClInclude Include="src\PCH.h" /> <ClInclude Include="src\PCH.h" />
<ClInclude Include="src\utils.h" />
<ClInclude Include="src\version.h" /> <ClInclude Include="src\version.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -30,6 +30,9 @@
<ClCompile Include="src\BRTransaction.cpp"> <ClCompile Include="src\BRTransaction.cpp">
<Filter>src</Filter> <Filter>src</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\utils.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="src\PCH.h"> <ClInclude Include="src\PCH.h">
@ -59,6 +62,12 @@
<ClInclude Include="src\BRTransaction.h"> <ClInclude Include="src\BRTransaction.h">
<Filter>src</Filter> <Filter>src</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\utils.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="src\FormIds.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include=".clang-format" /> <None Include=".clang-format" />

View File

@ -1,6 +1,67 @@
#include "bindings.h" #include "bindings.h"
#include "utils.h"
#include "NativeFunctions.h" #include "NativeFunctions.h"
#include "FormIds.h"
const float PI = 3.14159265358979f;
const std::map<uint32_t, uint32_t> shelf_types { {0x00603f, 1} };
const std::map<uint32_t, uint32_t> shelf_form_ids { {1, 0x00603f} };
struct Position {
float position_x;
float position_y;
float position_z;
float angle_x;
float angle_y;
float angle_z;
uint16_t scale;
};
struct Button {
uint32_t form_id;
Position position;
};
enum ButtonType { Toggle, Next, Previous };
const std::map<uint32_t, std::map<ButtonType, Button>> shelf_buttons {
{ 1, {
{ ButtonType::Next, Button {
0x002fab,
Position {
68.,
-25.,
83.,
PI / 2.,
PI,
PI,
50
}
} },
{ ButtonType::Previous, Button {
0x002fac,
Position {
-68.,
-25.,
83.,
PI / 2.,
PI,
PI,
50
}
} },
{ ButtonType::Toggle, Button {
0x002fad,
Position {
0.,
-27.,
157.,
PI / 2.,
PI,
PI,
50
}
} },
} }
};
const std::set<uint32_t> ignored_shelf_related_form_ids {0x002fab, 0x002fac, 0x002fad};
void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESQuest* quest) { void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESQuest* quest) {
logger::info("Entered CreateInteriorRefListImpl"); logger::info("Entered CreateInteriorRefListImpl");
@ -27,6 +88,7 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton(); RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
std::vector<RawInteriorRef> raw_interior_refs; std::vector<RawInteriorRef> raw_interior_refs;
std::vector<RawShelf> raw_shelves;
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry) { for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry) {
RE::TESObjectREFR * ref = (*entry).get(); RE::TESObjectREFR * ref = (*entry).get();
const char * name = ref->GetName(); const char * name = ref->GetName();
@ -47,12 +109,6 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
float angle_x = ref->GetAngleX(); float angle_x = ref->GetAngleX();
float angle_y = ref->GetAngleY(); float angle_y = ref->GetAngleY();
float angle_z = ref->GetAngleZ(); float angle_z = ref->GetAngleZ();
RE::NiNPShortPoint3 boundMin = base->boundData.boundMin;
RE::NiNPShortPoint3 boundMax = base->boundData.boundMax;
uint16_t bound_x = boundMax.x > boundMin.x ? boundMax.x - boundMin.x : boundMin.x - boundMax.x;
uint16_t bound_y = boundMax.y > boundMin.y ? boundMax.y - boundMin.y : boundMin.y - boundMax.y;
uint16_t bound_z = boundMax.z > boundMin.z ? boundMax.z - boundMin.z : boundMin.z - boundMax.z;
logger::info(FMT_STRING("CreateInteriorRefList bounds: width: {:d}, length: {:d}, height: {:d}"), bound_x, bound_y, bound_z);
uint16_t scale = ref->refScale; uint16_t scale = ref->refScale;
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 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)); logger::info(FMT_STRING("CreateInteriorRefList deleted: {:d}, wants delete: {:d}"), ref->IsMarkedForDeletion(), ref->inGameFormFlags.all(RE::TESObjectREFR::InGameFormFlag::kWantsDelete));
@ -76,6 +132,43 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
ref_file_name = _strdup(ref_file->fileName); ref_file_name = _strdup(ref_file->fileName);
} }
} }
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) {
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");
continue;
}
auto maybe_shelf_type = shelf_types.find(base_local_form_id);
if (maybe_shelf_type != shelf_types.end()) {
logger::info("CreateInteriorRefList ref is a shelf!");
uint32_t shelf_type = (*maybe_shelf_type).second;
// TODO: actually set these values based off the state of the shelf
uint32_t page = 1;
uint32_t filter_form_type = 0;
bool filter_is_food = false;
const char* search = nullptr;
const char* sort_on = nullptr;
bool sort_asc = true;
raw_shelves.push_back({
shelf_type,
position_x,
position_y,
position_z,
angle_x,
angle_y,
angle_z,
scale,
page,
filter_form_type,
filter_is_food,
search,
sort_on,
sort_asc,
});
continue;
}
}
raw_interior_refs.push_back({ raw_interior_refs.push_back({
base_file_name, base_file_name,
@ -93,7 +186,9 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
} }
} }
FFIResult<int32_t> result = update_interior_ref_list(api_url.c_str(), api_key.c_str(), shop_id, &raw_interior_refs[0], raw_interior_refs.size()); RawInteriorRef* refs_front = raw_interior_refs.empty() ? nullptr : &raw_interior_refs.front();
RawShelf* shelves_front = raw_shelves.empty() ? nullptr : &raw_shelves.front();
FFIResult<int32_t> result = update_interior_ref_list(api_url.c_str(), api_key.c_str(), shop_id, refs_front, raw_interior_refs.size(), shelves_front, raw_shelves.size());
if (result.IsOk()) { if (result.IsOk()) {
int32_t interior_ref_list_id = result.AsOk(); int32_t interior_ref_list_id = result.AsOk();
logger::info(FMT_STRING("CreateInteriorRefList success: {}"), interior_ref_list_id); logger::info(FMT_STRING("CreateInteriorRefList success: {}"), interior_ref_list_id);
@ -164,7 +259,7 @@ bool ClearCell(RE::StaticFunctionTag*) {
return true; return true;
} }
void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { void LoadRefsTask(FFIResult<RawInteriorRefData> result, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest) {
logger::info("Entered LoadRefsTask"); logger::info("Entered LoadRefsTask");
if (!quest) { if (!quest) {
@ -174,13 +269,21 @@ void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
// Placing the refs must be done on the main thread otherwise calling MoveTo causes a crash // Placing the refs must be done on the main thread otherwise calling MoveTo causes a crash
auto task = SKSE::GetTaskInterface(); auto task = SKSE::GetTaskInterface();
task->AddTask([result, target_ref, quest]() { task->AddTask([result, target_ref, private_chest, public_chest, quest]() {
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton(); RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
RE::BSScript::Internal::VirtualMachine * a_vm = RE::BSScript::Internal::VirtualMachine::GetSingleton(); RE::BSScript::Internal::VirtualMachine * a_vm = RE::BSScript::Internal::VirtualMachine::GetSingleton();
using func_t = decltype(&PlaceAtMe); using func_t = decltype(&PlaceAtMe);
REL::Relocation<func_t> PlaceAtMe_Native{ REL::ID(55672) }; REL::Relocation<func_t> PlaceAtMe_Native{ REL::ID(55672) };
using func_t2 = decltype(&MoveTo); using func_t2 = decltype(&MoveTo);
REL::Relocation<func_t2> MoveTo_Native(RE::Offset::TESObjectREFR::MoveTo); REL::Relocation<func_t2> MoveTo_Native(RE::Offset::TESObjectREFR::MoveTo);
REL::ID extra_linked_ref_vtbl(static_cast<std::uint64_t>(229564));
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);
RE::BGSKeyword* public_chest_keyword = data_handler->LookupForm<RE::BGSKeyword>(KEYWORD_PUBLIC_CHEST, MOD_NAME);
RE::BGSKeyword* toggle_keyword = data_handler->LookupForm<RE::BGSKeyword>(KEYWORD_TOGGLE, MOD_NAME);
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> successReg = SKSE::RegistrationMap<bool>();
successReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListSuccess")); successReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListSuccess"));
@ -194,6 +297,20 @@ void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
failReg.Unregister(quest); failReg.Unregister(quest);
return; return;
} }
if (!private_chest) {
logger::error("LoadRefsTask private_chest is null!");
failReg.SendEvent("Private merchant chest reference is null");
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
if (!public_chest) {
logger::error("LoadRefsTask public_chest is null!");
failReg.SendEvent("Public merchant chest reference is null");
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty"); RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
logger::info(FMT_STRING("LoadRefsImpl lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID()); logger::info(FMT_STRING("LoadRefsImpl lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
@ -207,11 +324,12 @@ void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
if (result.IsOk()) { if (result.IsOk()) {
logger::info("LoadInteriorRefList get_interior_ref_list result: OK"); logger::info("LoadInteriorRefList get_interior_ref_list result: OK");
RawInteriorRefVec vec = result.AsOk(); RawInteriorRefData data = result.AsOk();
logger::info(FMT_STRING("LoadInteriorRefList vec len: {:d}, cap: {:d}"), vec.len, vec.cap); RawInteriorRefVec ref_vec = data.interior_ref_vec;
logger::info(FMT_STRING("LoadInteriorRefList refs_vec len: {:d}, cap: {:d}"), ref_vec.len, ref_vec.cap);
for (int i = 0; i < vec.len; i++) { for (int i = 0; i < ref_vec.len; i++) {
RawInteriorRef ref = vec.ptr[i]; RawInteriorRef ref = ref_vec.ptr[i];
logger::info(FMT_STRING("LoadInteriorRefList ref base_mod_name: {}, base_local_form_id: {:x}"), ref.base_mod_name, ref.base_local_form_id); logger::info(FMT_STRING("LoadInteriorRefList ref base_mod_name: {}, base_local_form_id: {:x}"), ref.base_mod_name, ref.base_local_form_id);
logger::info(FMT_STRING("LoadInteriorRefList ref position {:.2f} {:.2f} {:.2f}, angle: {:.2f} {:.2f} {:.2f}, scale: {:d}"), ref.position_x, ref.position_y, ref.position_z, ref.angle_x, ref.angle_y, ref.angle_z, ref.scale); logger::info(FMT_STRING("LoadInteriorRefList ref position {:.2f} {:.2f} {:.2f}, angle: {:.2f} {:.2f} {:.2f}, scale: {:d}"), ref.position_x, ref.position_y, ref.position_z, ref.angle_x, ref.angle_y, ref.angle_z, ref.scale);
if (strcmp(ref.base_mod_name, "Skyrim.esm") == 0 && ref.base_local_form_id == 7) { if (strcmp(ref.base_mod_name, "Skyrim.esm") == 0 && ref.base_local_form_id == 7) {
@ -251,6 +369,87 @@ void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
MoveTo_Native(game_ref, game_ref->CreateRefHandle(), cell, cell->worldSpace, position, angle); MoveTo_Native(game_ref, game_ref->CreateRefHandle(), cell, cell->worldSpace, position, angle);
game_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell game_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
} }
RawShelfVec shelf_vec = data.shelf_vec;
logger::info(FMT_STRING("LoadInteriorRefList shelf_vec len: {:d}, cap: {:d}"), shelf_vec.len, shelf_vec.cap);
for (int i = 0; i < shelf_vec.len; i++) {
RawShelf shelf = shelf_vec.ptr[i];
logger::info(FMT_STRING("LoadInteriorRefList shelf shelf_type: {}"), shelf.shelf_type);
logger::info(FMT_STRING("LoadInteriorRefList shelf position {:.2f} {:.2f} {:.2f}, angle: {:.2f} {:.2f} {:.2f}, scale: {:d}"), shelf.position_x, shelf.position_y, shelf.position_z, shelf.angle_x, shelf.angle_y, shelf.angle_z, shelf.scale);
RE::NiPoint3 position = RE::NiPoint3::NiPoint3(shelf.position_x, shelf.position_y, shelf.position_z);
RE::NiPoint3 angle = RE::NiPoint3::NiPoint3(shelf.angle_x, shelf.angle_y, shelf.angle_z);
auto maybe_form_id = shelf_form_ids.find(shelf.shelf_type);
if (maybe_form_id != shelf_form_ids.end()) {
logger::info(FMT_STRING("LoadInteriorRefList shelf form_id: {}"), (*maybe_form_id).second);
RE::TESForm* form = data_handler->LookupForm((*maybe_form_id).second, MOD_NAME);
if (!form) {
logger::error("LoadInteriorRefList failed to find shelf base form!");
failReg.SendEvent("Failed to place a shelf into the cell, could not find shelf base form");
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
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());
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);
const std::map<ButtonType, RE::BGSKeyword*> button_type_to_keyword = {
{ ButtonType::Toggle, toggle_keyword },
{ ButtonType::Next, next_keyword },
{ ButtonType::Previous, prev_keyword },
};
RE::NiMatrix3 rotation_matrix = get_rotation_matrix(angle);
auto maybe_buttons = shelf_buttons.find(shelf.shelf_type);
if (maybe_buttons != shelf_buttons.end()) {
std::map<ButtonType, Button> buttons = (*maybe_buttons).second;
for (auto entry = buttons.begin(); entry != buttons.end(); ++entry) {
ButtonType button_type = (*entry).first;
Button button = (*entry).second;
RE::TESForm* button_form = data_handler->LookupForm(button.form_id, MOD_NAME);
if (!button_form) {
logger::error("LoadInteriorRefList failed to find shelf button base form!");
failReg.SendEvent("Failed to place a shelf button into the cell, could not find shelf button base form");
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
RE::NiPoint3 button_position = RE::NiPoint3::NiPoint3(button.position.position_x, button.position.position_y, button.position.position_z);
RE::NiPoint3 rotated_position = rotate_point(button_position, rotation_matrix);
button_position = RE::NiPoint3::NiPoint3(position.x + rotated_position.x, position.y + rotated_position.y, position.z + rotated_position.z);
RE::NiPoint3 button_angle = RE::NiPoint3::NiPoint3(angle.x + button.position.angle_x, angle.z + button.position.angle_y, angle.y + button.position.angle_z);
logger::info(FMT_STRING("LoadInteriorRefList (adjusted) button position_x: {}, position_y: {}, position_z: {}, angle_x: {}, angle_y: {}, angle_z: {}"),
button_position.x, button_position.y, button_position.z, button_angle.x, button_angle.y, button_angle.z);
RE::TESObjectREFR* button_ref = PlaceAtMe_Native(a_vm, 0, target_ref, button_form, 1, false, false);
MoveTo_Native(button_ref, button_ref->CreateRefHandle(), cell, cell->worldSpace, button_position, button_angle);
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());
button_extra_linked_ref->linkedRefs.push_back({shelf_keyword, shelf_ref});
button_ref->extraList.Add(button_extra_linked_ref);
auto maybe_keyword = button_type_to_keyword.find(button_type);
if (maybe_keyword != button_type_to_keyword.end()) {
RE::BGSKeyword* keyword = (*maybe_keyword).second;
shelf_extra_linked_ref->linkedRefs.push_back({keyword, button_ref});
}
}
} else {
logger::warn(FMT_STRING("LoadInteriorRefList found no buttons for shelf_type: {}"), shelf.shelf_type);
}
} else {
logger::error("LoadInteriorRefList unrecognized shelf type!");
failReg.SendEvent(fmt::format(FMT_STRING("Failed to place a shelf into the cell, unrecognized shelf_type: {}"), shelf.shelf_type));
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
}
} else { } else {
const char * error = result.AsErr(); const char * error = result.AsErr();
logger::error(FMT_STRING("LoadInteriorRefList get_interior_ref_list error: {}"), error); logger::error(FMT_STRING("LoadInteriorRefList get_interior_ref_list error: {}"), error);
@ -266,48 +465,64 @@ void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
}); });
} }
void LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { void LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefListImpl"); logger::info("Entered LoadInteriorRefListImpl");
LoadRefsTask(get_interior_ref_list(api_url.c_str(), api_key.c_str(), interior_ref_list_id), target_ref, quest); LoadRefsTask(get_interior_ref_list(api_url.c_str(), api_key.c_str(), interior_ref_list_id), target_ref, private_chest, public_chest, quest);
} }
bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefList"); logger::info("Entered LoadInteriorRefList");
if (!target_ref) { if (!target_ref) {
logger::error("LoadInteriorRefList target_ref is null!"); logger::error("LoadInteriorRefList target_ref is null!");
return false; return false;
} }
if (!private_chest) {
logger::error("LoadInteriorRefList private_chest is null!");
return false;
}
if (!public_chest) {
logger::error("LoadInteriorRefList public_chest is null!");
return false;
}
if (!quest) { if (!quest) {
logger::error("LoadInteriorRefList quest is null!"); logger::error("LoadInteriorRefList quest is null!");
return false; return false;
} }
std::thread thread(LoadInteriorRefListImpl, api_url, api_key, interior_ref_list_id, target_ref, quest); std::thread thread(LoadInteriorRefListImpl, api_url, api_key, interior_ref_list_id, target_ref, private_chest, public_chest, quest);
thread.detach(); thread.detach();
return true; return true;
} }
void LoadInteriorRefListByShopIdImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { void LoadInteriorRefListByShopIdImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefListByShopIdImpl"); logger::info("Entered LoadInteriorRefListByShopIdImpl");
LoadRefsTask(get_interior_ref_list_by_shop_id(api_url.c_str(), api_key.c_str(), shop_id), target_ref, quest); LoadRefsTask(get_interior_ref_list_by_shop_id(api_url.c_str(), api_key.c_str(), shop_id), target_ref, private_chest, public_chest, quest);
} }
bool LoadInteriorRefListByShopId(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { bool LoadInteriorRefListByShopId(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefListByShopId"); logger::info("Entered LoadInteriorRefListByShopId");
if (!target_ref) { if (!target_ref) {
logger::error("LoadInteriorRefListByShopId target_ref is null!"); logger::error("LoadInteriorRefListByShopId target_ref is null!");
return false; return false;
} }
if (!private_chest) {
logger::error("LoadInteriorRefListByShopId private_chest is null!");
return false;
}
if (!public_chest) {
logger::error("LoadInteriorRefListByShopId public_chest is null!");
return false;
}
if (!quest) { if (!quest) {
logger::error("LoadInteriorRefListByShopId quest is null!"); logger::error("LoadInteriorRefListByShopId quest is null!");
return false; return false;
} }
std::thread thread(LoadInteriorRefListByShopIdImpl, api_url, api_key, shop_id, target_ref, quest); std::thread thread(LoadInteriorRefListByShopIdImpl, api_url, api_key, shop_id, target_ref, private_chest, public_chest, quest);
thread.detach(); thread.detach();
return true; return true;
} }

View File

@ -2,5 +2,5 @@
bool CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESQuest* quest); bool CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESQuest* quest);
bool ClearCell(RE::StaticFunctionTag*); bool ClearCell(RE::StaticFunctionTag*);
bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest); bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest);
bool LoadInteriorRefListByShopId(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest); bool LoadInteriorRefListByShopId(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, int32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESObjectREFR* private_chest, RE::TESObjectREFR* public_chest, RE::TESQuest* quest);

View File

@ -1,5 +1,6 @@
#include "bindings.h" #include "bindings.h"
#include "utils.h"
#include "NativeFunctions.h" #include "NativeFunctions.h"
class HaggleVisitor : public RE::PerkEntryVisitor { class HaggleVisitor : public RE::PerkEntryVisitor {
@ -97,28 +98,6 @@ bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR*
return true; return true;
} }
RE::NiMatrix3 get_rotation_matrix(RE::NiPoint3 rotation) {
RE::NiMatrix3 rotation_matrix = RE::NiMatrix3();
rotation_matrix.entry[0][0] = cos(rotation.z) * cos(rotation.y);
rotation_matrix.entry[0][1] = (cos(rotation.z) * sin(rotation.y) * sin(rotation.x)) - (sin(rotation.z) * cos(rotation.x));
rotation_matrix.entry[0][2] = (cos(rotation.z) * sin(rotation.y) * cos(rotation.x)) + (sin(rotation.z) * sin(rotation.x));
rotation_matrix.entry[1][0] = sin(rotation.z) * cos(rotation.y);
rotation_matrix.entry[1][1] = (sin(rotation.z) * sin(rotation.y) * sin(rotation.x)) + (cos(rotation.z) * cos(rotation.x));
rotation_matrix.entry[1][2] = (sin(rotation.z) * sin(rotation.y) * cos(rotation.x)) - (cos(rotation.z) * sin(rotation.x));
rotation_matrix.entry[2][0] = sin(rotation.y) * -1;
rotation_matrix.entry[2][1] = cos(rotation.y) * sin(rotation.x);
rotation_matrix.entry[2][2] = cos(rotation.y) * cos(rotation.x);
return rotation_matrix;
}
RE::NiPoint3 rotate_point(RE::NiPoint3 point, RE::NiMatrix3 rotation_matrix) {
return RE::NiPoint3(
(point.x * rotation_matrix.entry[0][0]) + (point.y * rotation_matrix.entry[1][0]) + (point.z * rotation_matrix.entry[2][0]),
(point.x * rotation_matrix.entry[0][1]) + (point.y * rotation_matrix.entry[1][1]) + (point.z * rotation_matrix.entry[2][1]),
(point.x * rotation_matrix.entry[0][2]) + (point.y * rotation_matrix.entry[1][2]) + (point.z * rotation_matrix.entry[2][2])
);
}
void LoadMerchTask( void LoadMerchTask(
FFIResult<RawMerchandiseVec> result, FFIResult<RawMerchandiseVec> result,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,

10
src/FormIds.h Normal file
View File

@ -0,0 +1,10 @@
#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;

21
src/utils.cpp Normal file
View File

@ -0,0 +1,21 @@
RE::NiMatrix3 get_rotation_matrix(RE::NiPoint3 rotation) {
RE::NiMatrix3 rotation_matrix = RE::NiMatrix3();
rotation_matrix.entry[0][0] = cos(rotation.z) * cos(rotation.y);
rotation_matrix.entry[0][1] = (cos(rotation.z) * sin(rotation.y) * sin(rotation.x)) - (sin(rotation.z) * cos(rotation.x));
rotation_matrix.entry[0][2] = (cos(rotation.z) * sin(rotation.y) * cos(rotation.x)) + (sin(rotation.z) * sin(rotation.x));
rotation_matrix.entry[1][0] = sin(rotation.z) * cos(rotation.y);
rotation_matrix.entry[1][1] = (sin(rotation.z) * sin(rotation.y) * sin(rotation.x)) + (cos(rotation.z) * cos(rotation.x));
rotation_matrix.entry[1][2] = (sin(rotation.z) * sin(rotation.y) * cos(rotation.x)) - (cos(rotation.z) * sin(rotation.x));
rotation_matrix.entry[2][0] = sin(rotation.y) * -1;
rotation_matrix.entry[2][1] = cos(rotation.y) * sin(rotation.x);
rotation_matrix.entry[2][2] = cos(rotation.y) * cos(rotation.x);
return rotation_matrix;
}
RE::NiPoint3 rotate_point(RE::NiPoint3 point, RE::NiMatrix3 rotation_matrix) {
return RE::NiPoint3(
(point.x * rotation_matrix.entry[0][0]) + (point.y * rotation_matrix.entry[1][0]) + (point.z * rotation_matrix.entry[2][0]),
(point.x * rotation_matrix.entry[0][1]) + (point.y * rotation_matrix.entry[1][1]) + (point.z * rotation_matrix.entry[2][1]),
(point.x * rotation_matrix.entry[0][2]) + (point.y * rotation_matrix.entry[1][2]) + (point.z * rotation_matrix.entry[2][2])
);
}

3
src/utils.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
RE::NiMatrix3 get_rotation_matrix(RE::NiPoint3 rotation);
RE::NiPoint3 rotate_point(RE::NiPoint3 point, RE::NiMatrix3 rotation_matrix);