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:
parent
0f1b017158
commit
e673ac4642
@ -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>
|
||||||
|
@ -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" />
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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
10
src/FormIds.h
Normal 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
21
src/utils.cpp
Normal 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
3
src/utils.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user