Update interior_ref_list and merchandise_list by shop id

Also, rename placeholder to activator. Get merchandise by shop id. Start adjusting merch positions based on shelf angle.
This commit is contained in:
Tyler Hallada 2020-10-25 03:18:27 -04:00
parent 6512534219
commit cf8e0d099d
3 changed files with 168 additions and 86 deletions

View File

@ -93,7 +93,7 @@ void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
} }
} }
FFIResult<int32_t> result = create_interior_ref_list(api_url.c_str(), api_key.c_str(), shop_id, &raw_interior_refs[0], raw_interior_refs.size()); 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());
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,11 +164,11 @@ bool ClearCell(RE::StaticFunctionTag*) {
return true; return true;
} }
void LoadRefsImpl(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { void LoadRefsTask(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {
logger::info("Entered LoadRefsImpl"); logger::info("Entered LoadRefsTask");
if (!quest) { if (!quest) {
logger::error("LoadRefsImpl quest is null!"); logger::error("LoadRefsTask quest is null!");
return; return;
} }
@ -188,7 +188,7 @@ void LoadRefsImpl(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
failReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListFail")); failReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListFail"));
if (!target_ref) { if (!target_ref) {
logger::error("LoadRefsImpl target_ref is null!"); logger::error("LoadRefsTask target_ref is null!");
failReg.SendEvent("Spawn target reference is null"); failReg.SendEvent("Spawn target reference is null");
successReg.Unregister(quest); successReg.Unregister(quest);
failReg.Unregister(quest); failReg.Unregister(quest);
@ -198,7 +198,7 @@ void LoadRefsImpl(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
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());
if (!cell) { if (!cell) {
logger::error("LoadRefsImpl cell is null!"); logger::error("LoadRefsTask cell is null!");
failReg.SendEvent("Lookup failed for the shop Cell: BREmpty"); failReg.SendEvent("Lookup failed for the shop Cell: BREmpty");
successReg.Unregister(quest); successReg.Unregister(quest);
failReg.Unregister(quest); failReg.Unregister(quest);
@ -269,7 +269,7 @@ void LoadRefsImpl(FFIResult<RawInteriorRefVec> result, RE::TESObjectREFR* target
void LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { void LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefListImpl"); logger::info("Entered LoadInteriorRefListImpl");
LoadRefsImpl(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, quest);
} }
bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {
@ -292,7 +292,7 @@ bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::
void LoadInteriorRefListByShopIdImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { void LoadInteriorRefListByShopIdImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefListByShopIdImpl"); logger::info("Entered LoadInteriorRefListByShopIdImpl");
LoadRefsImpl(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, quest);
} }
bool LoadInteriorRefListByShopId(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) { bool LoadInteriorRefListByShopId(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {

View File

@ -2,14 +2,13 @@
#include "NativeFunctions.h" #include "NativeFunctions.h"
// TODO: should I replace "placeholder" with "buy_activator" and "ref" with "item_ref"? bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR* merchant_shelf, RE::TESForm* activator_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword, RE::BGSKeyword* activator_keyword)
bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword * shelf_keyword, RE::BGSKeyword * item_keyword)
{ {
logger::info("Entered ClearMerchandiseImpl"); logger::info("Entered ClearMerchandiseImpl");
if (merchant_chest && merchant_shelf) { if (merchant_chest && merchant_shelf) {
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell(); RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
RE::FormID placeholder_form_id = placeholder_static->GetFormID(); RE::FormID activator_form_id = activator_static->GetFormID();
RE::FormID shelf_form_id = merchant_shelf->GetFormID(); RE::FormID shelf_form_id = merchant_shelf->GetFormID();
for (auto entry = cell->references.begin(); entry != cell->references.end();) for (auto entry = cell->references.begin(); entry != cell->references.end();)
{ {
@ -18,19 +17,19 @@ bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR*
RE::TESBoundObject * base = ref->GetBaseObject(); RE::TESBoundObject * base = ref->GetBaseObject();
if (base) { if (base) {
RE::FormID form_id = base->GetFormID(); RE::FormID form_id = base->GetFormID();
if (form_id == placeholder_form_id) { if (form_id == activator_form_id) {
logger::info("ClearMerchandise found placeholder ref"); logger::info("ClearMerchandise found activator ref");
RE::TESObjectREFR * shelf_linked_ref = ref->GetLinkedRef(shelf_keyword); RE::TESObjectREFR * shelf_linked_ref = ref->GetLinkedRef(shelf_keyword);
if (shelf_linked_ref && shelf_linked_ref->GetFormID() == shelf_form_id) { if (shelf_linked_ref && shelf_linked_ref->GetFormID() == shelf_form_id) {
logger::info("ClearMerchandise placeholder ref is linked with cleared shelf"); logger::info("ClearMerchandise activator ref is linked with cleared shelf");
RE::TESObjectREFR * linked_ref = ref->GetLinkedRef(item_keyword); RE::TESObjectREFR * linked_ref = ref->GetLinkedRef(item_keyword);
if (linked_ref) { if (linked_ref) {
logger::info(FMT_STRING("ClearMerchandise deleting ref linked to placeholder ref: {:x}"), (uint32_t)linked_ref->GetFormID()); logger::info(FMT_STRING("ClearMerchandise deleting ref linked to activator ref: {:x}"), (uint32_t)linked_ref->GetFormID());
// TODO: should I use the MemoryManager to free these references? // TODO: should I use the MemoryManager to free these references?
linked_ref->Disable(); // disabling first is required to prevent CTD on unloading cell linked_ref->Disable(); // disabling first is required to prevent CTD on unloading cell
linked_ref->SetDelete(true); linked_ref->SetDelete(true);
} }
logger::info(FMT_STRING("ClearMerchandise deleting existing placeholder ref: {:x}"), (uint32_t)ref->GetFormID()); logger::info(FMT_STRING("ClearMerchandise deleting existing activator ref: {:x}"), (uint32_t)ref->GetFormID());
ref->Disable(); // disabling first is required to prevent CTD on unloading cell ref->Disable(); // disabling first is required to prevent CTD on unloading cell
ref->SetDelete(true); ref->SetDelete(true);
cell->references.erase(*entry++); // prevents slowdowns after many runs of ClearMerchandise cell->references.erase(*entry++); // prevents slowdowns after many runs of ClearMerchandise
@ -59,46 +58,33 @@ bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR*
return true; return true;
} }
void LoadMerchandiseImpl( void LoadMerchTask(
RE::BSFixedString api_url, FFIResult<RawMerchandiseVec> result,
RE::BSFixedString api_key,
uint32_t merchandise_list_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword, RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword, RE::BGSKeyword* prev_keyword,
int page int page
) { ) {
logger::info("Entered LoadMerchandiseImpl");
logger::info(FMT_STRING("LoadMerchandise page: {:d}"), page);
if (!merchant_shelf) { if (!merchant_shelf) {
logger::error("LoadMerchandise merchant_shelf is null!"); logger::error("LoadMerchTask merchant_shelf is null!");
return; return;
} }
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
RE::TESObjectREFR * toggle_ref = merchant_shelf->GetLinkedRef(toggle_keyword); RE::TESObjectREFR * toggle_ref = merchant_shelf->GetLinkedRef(toggle_keyword);
if (!toggle_ref) { if (!toggle_ref) {
logger::error("LoadMerchandise toggle_ref is null!"); logger::error("LoadMerchTask toggle_ref is null!");
return; return;
} }
RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword);
if (!merchant_chest) {
logger::error("LoadMerchandise merchant_chest is null!");
return;
}
FFIResult<RawMerchandiseVec> result = get_merchandise_list(api_url.c_str(), api_key.c_str(), merchandise_list_id);
// Placing the refs must be done on the main thread otherwise disabling & deleting refs in ClearMerchandiseImpl causes a crash // Placing the refs must be done on the main thread otherwise disabling & deleting refs in ClearMerchandiseImpl causes a crash
auto task = SKSE::GetTaskInterface(); auto task = SKSE::GetTaskInterface();
task->AddTask([result, merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword, prev_keyword, next_keyword, page, toggle_ref, cell]() { task->AddTask([result, merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, prev_keyword, next_keyword, page, toggle_ref]() {
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);
@ -113,11 +99,21 @@ void LoadMerchandiseImpl(
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>(); SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(toggle_ref, RE::BSFixedString("OnLoadMerchandiseFail")); failReg.Register(toggle_ref, RE::BSFixedString("OnLoadMerchandiseFail"));
RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword);
if (!merchant_chest) {
logger::error("LoadMerchTask merchant_chest is null!");
failReg.SendEvent("Merchant chest reference is null");
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
return;
}
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
if (result.IsOk()) { if (result.IsOk()) {
logger::info("LoadMerchandise get_merchandise_list result OK"); logger::info("LoadMerchandise get_merchandise_list result OK");
RawMerchandiseVec vec = result.AsOk(); RawMerchandiseVec vec = result.AsOk();
logger::info(FMT_STRING("LoadMerchandise vec len: {:d}, cap: {:d}"), vec.len, vec.cap); logger::info(FMT_STRING("LoadMerchandise vec len: {:d}, cap: {:d}"), vec.len, vec.cap);
int max_page = std::ceil((float)(vec.len - 1) / (float)9); int max_page = std::ceil((float)(vec.len) / (float)9);
if (vec.len > 0 && page > max_page) { if (vec.len > 0 && page > max_page) {
logger::info(FMT_STRING("LoadMerchandise page {:d} is greater than max_page {:d}, doing nothing"), page, max_page); logger::info(FMT_STRING("LoadMerchandise page {:d} is greater than max_page {:d}, doing nothing"), page, max_page);
@ -127,7 +123,7 @@ void LoadMerchandiseImpl(
return; return;
} }
if (!ClearMerchandiseImpl(merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword)) { if (!ClearMerchandiseImpl(merchant_chest, merchant_shelf, activator_static, shelf_keyword, item_keyword, activator_keyword)) {
logger::error("LoadMerchandise ClearMerchandiseImpl returned a fail code"); logger::error("LoadMerchandise ClearMerchandiseImpl returned a fail code");
failReg.SendEvent(RE::BSFixedString("Failed to clear existing merchandise from shelf")); failReg.SendEvent(RE::BSFixedString("Failed to clear existing merchandise from shelf"));
successReg.Unregister(toggle_ref); successReg.Unregister(toggle_ref);
@ -159,20 +155,23 @@ void LoadMerchandiseImpl(
uint16_t bound_z = boundMax.z > boundMin.z ? boundMax.z - boundMin.z : boundMin.z - boundMax.z; uint16_t bound_z = boundMax.z > boundMin.z ? boundMax.z - boundMin.z : boundMin.z - boundMax.z;
logger::info(FMT_STRING("LoadMerchandise ref bounds width: {:d}, length: {:d}, height: {:d}"), bound_x, bound_y, bound_z); logger::info(FMT_STRING("LoadMerchandise ref bounds width: {:d}, length: {:d}, height: {:d}"), bound_x, bound_y, bound_z);
RE::TESObjectREFR * placeholder_ref = PlaceAtMe_Native(a_vm, 0, merchant_shelf, placeholder_static, 1, false, false); RE::TESObjectREFR * activator_ref = PlaceAtMe_Native(a_vm, 0, merchant_shelf, activator_static, 1, false, false);
RE::NiPoint3 bound_min = ref->GetBoundMin(); RE::NiPoint3 bound_min = ref->GetBoundMin();
RE::NiPoint3 bound_max = ref->GetBoundMax(); RE::NiPoint3 bound_max = ref->GetBoundMax();
logger::info(FMT_STRING("LoadMerchandise ref bounds min: {:.2f} {:.2f} {:.2f}, max: {:.2f} {:.2f} {:.2f}"), bound_min.x, bound_min.y, bound_min.z, bound_max.x, bound_max.y, bound_max.z); logger::info(FMT_STRING("LoadMerchandise ref bounds min: {:.2f} {:.2f} {:.2f}, max: {:.2f} {:.2f} {:.2f}"), bound_min.x, bound_min.y, bound_min.z, bound_max.x, bound_max.y, bound_max.z);
RE::ExtraLinkedRef * extra_linked_ref = (RE::ExtraLinkedRef*)RE::BSExtraData::Create(sizeof(RE::ExtraLinkedRef), extra_linked_ref_vtbl.address()); RE::ExtraLinkedRef * activator_extra_linked_ref = (RE::ExtraLinkedRef*)RE::BSExtraData::Create(sizeof(RE::ExtraLinkedRef), extra_linked_ref_vtbl.address());
extra_linked_ref->linkedRefs.push_back({shelf_keyword, merchant_shelf}); activator_extra_linked_ref->linkedRefs.push_back({shelf_keyword, merchant_shelf});
placeholder_ref->extraList.Add(extra_linked_ref); activator_ref->extraList.Add(activator_extra_linked_ref);
// This extra count stored on the placeholder_ref indicates the quanity of the merchandise item it is linked to RE::ExtraLinkedRef * item_extra_linked_ref = (RE::ExtraLinkedRef*)RE::BSExtraData::Create(sizeof(RE::ExtraLinkedRef), extra_linked_ref_vtbl.address());
ref->extraList.Add(item_extra_linked_ref);
// This extra count stored on the activator_ref indicates the quanity of the merchandise item it is linked to
RE::ExtraCount * extra_page_num = (RE::ExtraCount*)RE::BSExtraData::Create(sizeof(RE::ExtraCount), RE::Offset::ExtraCount::Vtbl.address()); RE::ExtraCount * extra_page_num = (RE::ExtraCount*)RE::BSExtraData::Create(sizeof(RE::ExtraCount), RE::Offset::ExtraCount::Vtbl.address());
extra_page_num->count = merch.quantity; extra_page_num->count = merch.quantity;
placeholder_ref->extraList.Add(extra_page_num); activator_ref->extraList.Add(extra_page_num);
float scale = 1; float scale = 1;
int max_over_bound = 0; int max_over_bound = 0;
@ -189,7 +188,7 @@ void LoadMerchandiseImpl(
scale = ((float)34 / (float)(max_over_bound + 34)) * (float)100; scale = ((float)34 / (float)(max_over_bound + 34)) * (float)100;
logger::info(FMT_STRING("LoadMerchandise new scale: {:.2f} {:d} (max_over_bound: {:d}"), scale, static_cast<uint16_t>(scale), max_over_bound); logger::info(FMT_STRING("LoadMerchandise new scale: {:.2f} {:d} (max_over_bound: {:d}"), scale, static_cast<uint16_t>(scale), max_over_bound);
ref->refScale = static_cast<uint16_t>(scale); ref->refScale = static_cast<uint16_t>(scale);
placeholder_ref->refScale = static_cast<uint16_t>(scale); activator_ref->refScale = static_cast<uint16_t>(scale);
} }
RE::NiPoint3 shelf_position = merchant_shelf->data.location; RE::NiPoint3 shelf_position = merchant_shelf->data.location;
@ -205,30 +204,31 @@ void LoadMerchandiseImpl(
} }
// TODO: make page size and buy_activator positions configurable per "shelf" type (where is config stored?) // TODO: make page size and buy_activator positions configurable per "shelf" type (where is config stored?)
if (i % 9 == 0) { if (i % 9 == 0) {
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + (-40 * cos(shelf_angle.z)) - (110 * cos(shelf_angle.x) * sin(shelf_angle.z)) + x_imbalance, shelf_position.y + (40 * sin(shelf_angle.z)) - (110 * sin(shelf_angle.x) * cos(shelf_angle.z)) + y_imbalance, shelf_position.z + (110 * cos(shelf_angle.x)) + z_imbalance);
} else if (i % 9 == 1) { } else if (i % 9 == 1) {
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
} else if (i % 9 == 2) { } else if (i % 9 == 2) {
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + (40 * cos(shelf_angle.z)) + x_imbalance, shelf_position.y + (-40 * sin(shelf_angle.z)) + y_imbalance, shelf_position.z + 110 + z_imbalance);
} else if (i % 9 == 3) { } else if (i % 9 == 3) {
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + (-40 * cos(shelf_angle.z)) + x_imbalance, shelf_position.y + (40 * sin(shelf_angle.z)) + y_imbalance, shelf_position.z + 65 + z_imbalance);
} else if (i % 9 == 4) { } else if (i % 9 == 4) {
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
} else if (i % 9 == 5) { } else if (i % 9 == 5) {
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + (40 * cos(shelf_angle.z)) + x_imbalance, shelf_position.y + (-40 * sin(shelf_angle.z)) + y_imbalance, shelf_position.z + 65 + z_imbalance);
} else if (i % 9 == 6) { } else if (i % 9 == 6) {
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + (-40 * cos(shelf_angle.z)) + x_imbalance, shelf_position.y + (40 * sin(shelf_angle.z)) + y_imbalance, shelf_position.z + 20 + z_imbalance);
} else if (i % 9 == 7) { } else if (i % 9 == 7) {
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
} else if (i % 9 == 8) { } else if (i % 9 == 8) {
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance); ref_position = RE::NiPoint3(shelf_position.x + (40 * cos(shelf_angle.z)) + x_imbalance, shelf_position.y + (-40 * sin(shelf_angle.z)) + y_imbalance, shelf_position.z + 20 + z_imbalance);
} }
MoveTo_Native(ref, ref->CreateRefHandle(), cell, cell->worldSpace, ref_position - RE::NiPoint3(10000, 10000, 10000), ref_angle); MoveTo_Native(ref, ref->CreateRefHandle(), cell, cell->worldSpace, ref_position - RE::NiPoint3(10000, 10000, 10000), ref_angle);
MoveTo_Native(placeholder_ref, placeholder_ref->CreateRefHandle(), cell, cell->worldSpace, ref_position, ref_angle); MoveTo_Native(activator_ref, ref->CreateRefHandle(), cell, cell->worldSpace, ref_position, ref_angle);
RE::BSFixedString name = RE::BSFixedString::BSFixedString(ref->GetName()); RE::BSFixedString name = RE::BSFixedString::BSFixedString(ref->GetName());
placeholder_ref->SetDisplayName(name, true); activator_ref->SetDisplayName(name, true);
placeholder_ref->extraList.SetOwner(base); // I'm abusing "owner" to link the activator with the Form that should be bought once activated activator_ref->extraList.SetOwner(base); // I'm abusing "owner" to link the activator with the Form that should be bought once activated
extra_linked_ref->linkedRefs.push_back({item_keyword, ref}); activator_extra_linked_ref->linkedRefs.push_back({item_keyword, ref});
item_extra_linked_ref->linkedRefs.push_back({activator_keyword, activator_ref});
} }
// I'm abusing the ExtraCount ExtraData type for storing the current page number state of the shelf // I'm abusing the ExtraCount ExtraData type for storing the current page number state of the shelf
@ -291,16 +291,59 @@ void LoadMerchandiseImpl(
}); });
} }
bool ToggleMerchandise( void LoadMerchandiseImpl(
RE::StaticFunctionTag*,
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
uint32_t merchandise_list_id, uint32_t merchandise_list_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword,
int page
) {
logger::info("Entered LoadMerchandiseImpl");
logger::info(FMT_STRING("LoadMerchandise page: {:d}"), page);
LoadMerchTask(get_merchandise_list(api_url.c_str(), api_key.c_str(), merchandise_list_id), merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, toggle_keyword, next_keyword, prev_keyword, page);
}
void LoadMerchandiseByShopIdImpl(
RE::BSFixedString api_url,
RE::BSFixedString api_key,
uint32_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,
int page
) {
logger::info("Entered LoadMerchandiseByShopIdImpl");
logger::info(FMT_STRING("LoadMerchandiseByShopIdImpl page: {:d}"), page);
LoadMerchTask(get_merchandise_list_by_shop_id(api_url.c_str(), api_key.c_str(), shop_id), merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, toggle_keyword, next_keyword, prev_keyword, page);
}
bool ToggleMerchandise(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
uint32_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* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword RE::BGSKeyword* prev_keyword
@ -316,7 +359,7 @@ bool ToggleMerchandise(
if (extra_is_loaded) { if (extra_is_loaded) {
// Clear merchandise // Clear merchandise
RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword); RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword);
if (!ClearMerchandiseImpl(merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword)) { if (!ClearMerchandiseImpl(merchant_chest, merchant_shelf, activator_static, shelf_keyword, item_keyword, activator_keyword)) {
return false; return false;
} }
@ -346,7 +389,7 @@ bool ToggleMerchandise(
} else { } else {
// Load merchandise // Load merchandise
int page = merchant_shelf->extraList.GetCount(); int page = merchant_shelf->extraList.GetCount();
std::thread thread(LoadMerchandiseImpl, api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page); std::thread thread(LoadMerchandiseByShopIdImpl, api_url, api_key, shop_id, merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, toggle_keyword, next_keyword, prev_keyword, page);
thread.detach(); thread.detach();
return true; return true;
} }
@ -356,12 +399,13 @@ bool LoadNextMerchandise(
RE::StaticFunctionTag*, RE::StaticFunctionTag*,
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
uint32_t merchandise_list_id, uint32_t shop_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword, RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword RE::BGSKeyword* prev_keyword
@ -378,7 +422,7 @@ bool LoadNextMerchandise(
page = page + 1; page = page + 1;
} }
std::thread thread(LoadMerchandiseImpl, api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page); std::thread thread(LoadMerchandiseByShopIdImpl, api_url, api_key, shop_id, merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, toggle_keyword, next_keyword, prev_keyword, page);
thread.detach(); thread.detach();
return true; return true;
} }
@ -387,12 +431,13 @@ bool LoadPrevMerchandise(
RE::StaticFunctionTag*, RE::StaticFunctionTag*,
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
uint32_t merchandise_list_id, uint32_t shop_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword, RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword RE::BGSKeyword* prev_keyword
@ -413,12 +458,46 @@ bool LoadPrevMerchandise(
page = page - 1; page = page - 1;
} }
std::thread thread(LoadMerchandiseImpl, api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page); std::thread thread(LoadMerchandiseByShopIdImpl, api_url, api_key, shop_id, merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, toggle_keyword, next_keyword, prev_keyword, page);
thread.detach(); thread.detach();
return true; return true;
} }
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword) { bool LoadMerchandiseByShopId(
RE::StaticFunctionTag*,
RE::BSFixedString api_url,
RE::BSFixedString api_key,
uint32_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
) {
if (!merchant_shelf) {
logger::error("LoadMerchandiseByShopId merchant_shelf is null!");
return false;
}
// I'm abusing the ExtraCannotWear ExtraData type as a boolean marker which stores whether the shelf is in a loaded or cleared state
// The presense of ExtraCannotWear == loaded, its absence == cleared
// Please don't try to wear the shelves :)
RE::ExtraCannotWear * extra_is_loaded = merchant_shelf->extraList.GetByType<RE::ExtraCannotWear>();
if (!extra_is_loaded) {
// Load merchandise
int page = merchant_shelf->extraList.GetCount();
std::thread thread(LoadMerchandiseByShopIdImpl, api_url, api_key, shop_id, merchant_shelf, activator_static, shelf_keyword, chest_keyword, item_keyword, activator_keyword, toggle_keyword, next_keyword, prev_keyword, page);
thread.detach();
return true;
} else {
logger::warn("LoadMerchandiseByShopId merchant shelf is marked as already loaded, not doing anything.");
}
}
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* activator_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword) {
logger::info("Entered ReplaceMerch3D"); logger::info("Entered ReplaceMerch3D");
if (!merchant_shelf) { if (!merchant_shelf) {
@ -426,22 +505,22 @@ bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, R
return false; return false;
} }
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell(); RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
RE::FormID placeholder_form_id = placeholder_static->GetFormID(); RE::FormID activator_form_id = activator_static->GetFormID();
RE::FormID shelf_form_id = merchant_shelf->GetFormID(); RE::FormID shelf_form_id = merchant_shelf->GetFormID();
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();
RE::TESBoundObject * base = ref->GetBaseObject(); RE::TESBoundObject * base = ref->GetBaseObject();
if (base) { if (base) {
if (base->GetFormID() == placeholder_form_id) { if (base->GetFormID() == activator_form_id) {
logger::info(FMT_STRING("ReplaceMerch3D REF is a placeholder ref: {:x}"), (uint32_t)ref->GetFormID()); logger::info(FMT_STRING("ReplaceMerch3D REF is a activator ref: {:x}"), (uint32_t)ref->GetFormID());
RE::TESObjectREFR * shelf_linked_ref = ref->GetLinkedRef(shelf_keyword); RE::TESObjectREFR * shelf_linked_ref = ref->GetLinkedRef(shelf_keyword);
if (shelf_linked_ref && shelf_linked_ref->GetFormID() == shelf_form_id) { if (shelf_linked_ref && shelf_linked_ref->GetFormID() == shelf_form_id) {
logger::info("ReplaceMerch3D placeholder ref is linked with loaded shelf"); logger::info("ReplaceMerch3D activator ref is linked with loaded shelf");
RE::TESObjectREFR * linked_ref = ref->GetLinkedRef(item_keyword); RE::TESObjectREFR * linked_ref = ref->GetLinkedRef(item_keyword);
if (linked_ref) { if (linked_ref) {
logger::info(FMT_STRING("ReplaceMerch3D placeholder has linked item ref: {:x}"), (uint32_t)linked_ref->GetFormID()); logger::info(FMT_STRING("ReplaceMerch3D activator has linked item ref: {:x}"), (uint32_t)linked_ref->GetFormID());
if (linked_ref->Is3DLoaded()) { if (linked_ref->Is3DLoaded()) {
logger::info("ReplaceMerch3D replaceing placeholder 3D with linked item 3D"); logger::info("ReplaceMerch3D replacing activator 3D with linked item 3D");
ref->Set3D(linked_ref->GetCurrent3D()); ref->Set3D(linked_ref->GetCurrent3D());
} else { } else {
logger::info("ReplaceMerch3D linked item ref 3D is not loaded yet, returning false"); logger::info("ReplaceMerch3D linked item ref 3D is not loaded yet, returning false");
@ -457,12 +536,12 @@ bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, R
return true; return true;
} }
RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchandise_placeholder) { RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchandise_activator) {
logger::info("Entered BuyMerchandise"); logger::info("Entered BuyMerchandise");
logger::info(FMT_STRING("BuyMerchandise activated ref: {}"), merchandise_placeholder->GetName()); logger::info(FMT_STRING("BuyMerchandise activated ref: {}"), merchandise_activator->GetName());
RE::TESForm * owner = merchandise_placeholder->GetOwner(); RE::TESForm * owner = merchandise_activator->GetOwner();
logger::info(FMT_STRING("BuyMerchandise owner: {}"), owner->GetName()); logger::info(FMT_STRING("BuyMerchandise owner: {}"), owner->GetName());
logger::info(FMT_STRING("BuyMerchandise count: {:d}"), merchandise_placeholder->extraList.GetCount()); logger::info(FMT_STRING("BuyMerchandise count: {:d}"), merchandise_activator->extraList.GetCount());
// TODO: do add item here // TODO: do add item here
return owner; return owner;
} }
@ -556,7 +635,7 @@ void CreateMerchandiseListImpl(RE::BSFixedString api_url, RE::BSFixedString api_
count++; count++;
} }
FFIResult<int32_t> result = create_merchandise_list(api_url.c_str(), api_key.c_str(), shop_id, &merch_records[0], merch_records.size()); FFIResult<int32_t> result = update_merchandise_list(api_url.c_str(), api_key.c_str(), shop_id, &merch_records[0], merch_records.size());
if (result.IsOk()) { if (result.IsOk()) {
int32_t merchandise_list_id = result.AsOk(); int32_t merchandise_list_id = result.AsOk();
logger::info(FMT_STRING("CreateMerchandiseList success: {}"), merchandise_list_id); logger::info(FMT_STRING("CreateMerchandiseList success: {}"), merchandise_list_id);

View File

@ -4,12 +4,13 @@ bool ToggleMerchandise(
RE::StaticFunctionTag*, RE::StaticFunctionTag*,
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
uint32_t merchandise_list_id, uint32_t shop_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword, RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword); RE::BGSKeyword* prev_keyword);
@ -17,12 +18,13 @@ bool LoadNextMerchandise(
RE::StaticFunctionTag*, RE::StaticFunctionTag*,
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
uint32_t merchandise_list_id, uint32_t shop_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword, RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword); RE::BGSKeyword* prev_keyword);
@ -30,15 +32,16 @@ bool LoadPrevMerchandise(
RE::StaticFunctionTag*, RE::StaticFunctionTag*,
RE::BSFixedString api_url, RE::BSFixedString api_url,
RE::BSFixedString api_key, RE::BSFixedString api_key,
uint32_t merchandise_list_id, uint32_t shop_id,
RE::TESObjectREFR* merchant_shelf, RE::TESObjectREFR* merchant_shelf,
RE::TESForm* placeholder_static, RE::TESForm* activator_static,
RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* shelf_keyword,
RE::BGSKeyword* chest_keyword, RE::BGSKeyword* chest_keyword,
RE::BGSKeyword* item_keyword, RE::BGSKeyword* item_keyword,
RE::BGSKeyword* activator_keyword,
RE::BGSKeyword* toggle_keyword, RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword, RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword); RE::BGSKeyword* prev_keyword);
bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* placeholder_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword); bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, RE::TESForm* activator_static, RE::BGSKeyword* shelf_keyword, RE::BGSKeyword* item_keyword);
RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchandise_placeholder); RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchandise_activator);
bool CreateMerchandiseList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest); bool CreateMerchandiseList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest);