From dc96fcba856ff4412985ae77268833dc08bb0441 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Fri, 23 Jun 2023 00:56:25 -0500 Subject: [PATCH] Vendor cleanup and start Donation Vendor impl --- dDatabase/Tables/CDItemComponentTable.cpp | 10 +-- dDatabase/Tables/CDItemComponentTable.h | 2 +- dGame/dComponents/CMakeLists.txt | 1 + dGame/dComponents/DonationVendorComponent.cpp | 24 ++++++ dGame/dComponents/DonationVendorComponent.h | 44 +++++++++++ dGame/dComponents/VendorComponent.cpp | 77 +++++++++---------- dGame/dComponents/VendorComponent.h | 59 +++++++------- dGame/dEntity/Entity.cpp | 3 + 8 files changed, 146 insertions(+), 74 deletions(-) create mode 100644 dGame/dComponents/DonationVendorComponent.cpp create mode 100644 dGame/dComponents/DonationVendorComponent.h diff --git a/dDatabase/Tables/CDItemComponentTable.cpp b/dDatabase/Tables/CDItemComponentTable.cpp index 54afc417..01f36062 100644 --- a/dDatabase/Tables/CDItemComponentTable.cpp +++ b/dDatabase/Tables/CDItemComponentTable.cpp @@ -74,8 +74,8 @@ CDItemComponentTable::CDItemComponentTable(void) { #endif } -const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) { - const auto& it = this->entries.find(skillID); +const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int id) { + const auto& it = this->entries.find(id); if (it != this->entries.end()) { return it->second; } @@ -83,11 +83,11 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s #ifndef CDCLIENT_CACHE_ALL std::stringstream query; - query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID); + query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(id); auto tableData = CDClientDatabase::ExecuteQuery(query.str()); if (tableData.eof()) { - entries.insert(std::make_pair(skillID, Default)); + entries.insert(std::make_pair(id, Default)); return Default; } @@ -140,7 +140,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s tableData.nextRow(); } - const auto& it2 = this->entries.find(skillID); + const auto& it2 = this->entries.find(id); if (it2 != this->entries.end()) { return it2->second; } diff --git a/dDatabase/Tables/CDItemComponentTable.h b/dDatabase/Tables/CDItemComponentTable.h index 11c34dd6..08761d61 100644 --- a/dDatabase/Tables/CDItemComponentTable.h +++ b/dDatabase/Tables/CDItemComponentTable.h @@ -58,7 +58,7 @@ public: static std::map ParseCraftingCurrencies(const CDItemComponent& itemComponent); // Gets an entry by ID - const CDItemComponent& GetItemComponentByID(unsigned int skillID); + const CDItemComponent& GetItemComponentByID(unsigned int id); static CDItemComponent Default; }; diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index e0a0f39b..89b263ed 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -7,6 +7,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "Component.cpp" "ControllablePhysicsComponent.cpp" "DestroyableComponent.cpp" + "DonationVendorComponent.cpp" "InventoryComponent.cpp" "ItemComponent.cpp" "LevelProgressionComponent.cpp" diff --git a/dGame/dComponents/DonationVendorComponent.cpp b/dGame/dComponents/DonationVendorComponent.cpp new file mode 100644 index 00000000..cd291402 --- /dev/null +++ b/dGame/dComponents/DonationVendorComponent.cpp @@ -0,0 +1,24 @@ +#include "DonationVendorComponent.h" + +DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) { + m_PercentComplete = 0.0; + m_TotalDonated = 0; + m_TotalRemaining = 0; +} + + + +void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { + VendorComponent::Serialize(outBitStream, bIsInitialUpdate, flags); + outBitStream->Write(bIsInitialUpdate || m_DirtyDonationVendor); + if (bIsInitialUpdate || m_DirtyDonationVendor) { + outBitStream->Write(m_PercentComplete); + outBitStream->Write(m_TotalDonated); + outBitStream->Write(m_TotalRemaining); + } +} + +void DonationVendorComponent::LoadConfigData() { + m_ActivityId = m_ParentEntity->GetVar(u"activityID"); + VendorComponent::LoadConfigData(); +} diff --git a/dGame/dComponents/DonationVendorComponent.h b/dGame/dComponents/DonationVendorComponent.h new file mode 100644 index 00000000..df6d3562 --- /dev/null +++ b/dGame/dComponents/DonationVendorComponent.h @@ -0,0 +1,44 @@ +#ifndef __DONATIONVENDORCOMPONENT__H__ +#define __DONATIONVENDORCOMPONENT__H__ + +#include "VendorComponent.h" +#include "eReplicaComponentType.h" + +class Entity; + +class DonationVendorComponent : public VendorComponent { +public: + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR; + DonationVendorComponent(Entity* parent); + + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void LoadConfigData() override; + + void SetPercentComplete(float percentComplete){ + if (m_PercentComplete == percentComplete) return; + m_PercentComplete = percentComplete; + m_DirtyDonationVendor = true; + } + + void SetTotalDonated(float totalDonated){ + if (m_TotalDonated == totalDonated) return; + m_TotalDonated = totalDonated; + m_DirtyDonationVendor = true; + } + + void SetTotalRemaining(float totalRemaining){ + if (m_TotalRemaining == totalRemaining) return; + m_TotalRemaining = totalRemaining; + m_DirtyDonationVendor = true; + } + +private: + bool m_DirtyDonationVendor = false; + float m_PercentComplete = 0.0; + int32_t m_TotalDonated = 0; + int32_t m_TotalRemaining = 0; + uint32_t m_ActivityId = 0; +}; + + +#endif //!__DONATIONVENDORCOMPONENT__H__ diff --git a/dGame/dComponents/VendorComponent.cpp b/dGame/dComponents/VendorComponent.cpp index 40b7f438..492241c9 100644 --- a/dGame/dComponents/VendorComponent.cpp +++ b/dGame/dComponents/VendorComponent.cpp @@ -9,18 +9,21 @@ #include "CDVendorComponentTable.h" #include "CDLootMatrixTable.h" #include "CDLootTableTable.h" +#include "CDItemComponentTable.h" VendorComponent::VendorComponent(Entity* parent) : Component(parent) { + m_HasStandardCostItems = false; + m_HasMultiCostItems = false; SetupConstants(); RefreshInventory(true); } -VendorComponent::~VendorComponent() = default; - void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - outBitStream->Write1(); - outBitStream->Write1(); // Has standard items (Required for vendors with missions.) - outBitStream->Write(HasCraftingStation()); // Has multi use items + outBitStream->Write(bIsInitialUpdate || m_DirtyVendor); + if (bIsInitialUpdate || m_DirtyVendor) { + outBitStream->Write(m_HasStandardCostItems); + outBitStream->Write(m_HasMultiCostItems); + } } void VendorComponent::OnUse(Entity* originator) { @@ -28,39 +31,19 @@ void VendorComponent::OnUse(Entity* originator) { GameMessages::SendVendorStatusUpdate(m_ParentEntity, originator->GetSystemAddress()); } -float VendorComponent::GetBuyScalar() const { - return m_BuyScalar; -} - -float VendorComponent::GetSellScalar() const { - return m_SellScalar; -} - -void VendorComponent::SetBuyScalar(float value) { - m_BuyScalar = value; -} - -void VendorComponent::SetSellScalar(float value) { - m_SellScalar = value; -} - -std::map& VendorComponent::GetInventory() { - return m_Inventory; -} - -bool VendorComponent::HasCraftingStation() { - // As far as we know, only Umami has a crafting station - return m_ParentEntity->GetLOT() == 13800; -} void VendorComponent::RefreshInventory(bool isCreation) { + SetHasStandardCostItems(false); + SetHasMultiCostItems(false); + //Custom code for Max vanity NPC if (m_ParentEntity->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) { if (!isCreation) return; - m_Inventory.insert({ 11909, 0 }); //Top hat w frog - m_Inventory.insert({ 7785, 0 }); //Flash bulb - m_Inventory.insert({ 12764, 0 }); //Big fountain soda - m_Inventory.insert({ 12241, 0 }); //Hot cocoa (from fb) + SetHasStandardCostItems(true); + m_Inventory.insert({ 11909, 0 }); // Top hat w frog + m_Inventory.insert({ 7785, 0 }); // Flash bulb + m_Inventory.insert({ 12764, 0 }); // Big fountain soda + m_Inventory.insert({ 12241, 0 }); // Hot cocoa (from fb) return; } m_Inventory.clear(); @@ -71,12 +54,20 @@ void VendorComponent::RefreshInventory(bool isCreation) { // Done with lootMatrix table auto* lootTableTable = CDClientManager::Instance().GetTable(); + auto* itemComponentTable = CDClientManager::Instance().GetTable(); + auto* compRegistryTable = CDClientManager::Instance().GetTable(); for (const auto& lootMatrix : lootMatrices) { int lootTableID = lootMatrix.LootTableIndex; std::vector vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); }); if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) { for (CDLootTable item : vendorItems) { + if (!m_HasStandardCostItems || !m_HasMultiCostItems){ + auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM); + auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); + if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); + if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); + } m_Inventory.insert({ item.itemid, item.sortPriority }); } } else { @@ -84,19 +75,21 @@ void VendorComponent::RefreshInventory(bool isCreation) { for (size_t i = 0; i < randomCount; i++) { if (vendorItems.empty()) break; - auto randomItemIndex = GeneralUtils::GenerateRandomNumber(0, vendorItems.size() - 1); - const auto& randomItem = vendorItems[randomItemIndex]; - vendorItems.erase(vendorItems.begin() + randomItemIndex); - + if (!m_HasStandardCostItems || !m_HasMultiCostItems){ + auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM); + auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); + if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); + if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); + } m_Inventory.insert({ randomItem.itemid, randomItem.sortPriority }); } } } - //Because I want a vendor to sell these cameras + //Because I (Max) want a vendor to sell these cameras if (m_ParentEntity->GetLOT() == 13569) { auto randomCamera = GeneralUtils::GenerateRandomNumber(0, 2); @@ -118,7 +111,9 @@ void VendorComponent::RefreshInventory(bool isCreation) { // Callback timer to refresh this inventory. m_ParentEntity->AddCallbackTimer(m_RefreshTimeSeconds, [this]() { RefreshInventory(); - }); + } + ); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); GameMessages::SendVendorStatusUpdate(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS); } @@ -135,6 +130,4 @@ void VendorComponent::SetupConstants() { m_LootMatrixID = vendorComps[0].LootMatrixIndex; } -bool VendorComponent::SellsItem(const LOT item) const { - return m_Inventory.find(item) != m_Inventory.end(); -} + diff --git a/dGame/dComponents/VendorComponent.h b/dGame/dComponents/VendorComponent.h index 0d9cd566..743d5b64 100644 --- a/dGame/dComponents/VendorComponent.h +++ b/dGame/dComponents/VendorComponent.h @@ -17,40 +17,41 @@ public: inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; VendorComponent(Entity* parent); - ~VendorComponent() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void OnUse(Entity* originator) override; - /** - * Gets the buy scaler - * @return the buy scaler - */ - float GetBuyScalar() const; + float GetBuyScalar() const { + return m_BuyScalar; + } - /** - * Sets the buy scalar. - * @param value the new value. - */ - void SetBuyScalar(float value); + float GetSellScalar() const { + return m_SellScalar; + } - /** - * Gets the buy scaler - * @return the buy scaler - */ - float GetSellScalar() const; + void SetBuyScalar(float value) { + m_BuyScalar = value; + } - /** - * Sets the sell scalar. - * @param value the new value. - */ - void SetSellScalar(float value); + void SetSellScalar(float value) { + m_SellScalar = value; + } - /** - * True if the NPC LOT is 13800, the only NPC with a crafting station. - */ - bool HasCraftingStation(); + std::map& GetInventory() { + return m_Inventory; + } + + void SetHasMultiCostItems(bool hasMultiCostItems) { + if (m_HasMultiCostItems == hasMultiCostItems) return; + m_HasMultiCostItems = hasMultiCostItems; + m_DirtyVendor = true; + } + void SetHasStandardCostItems(bool hasStandardCostItems) { + if (m_HasStandardCostItems == hasStandardCostItems) return; + m_HasStandardCostItems = hasStandardCostItems; + m_DirtyVendor = true; + } /** * Gets the list if items the vendor sells. @@ -68,7 +69,9 @@ public: */ void SetupConstants(); - bool SellsItem(const LOT item) const; + bool SellsItem(const LOT item) const { + return m_Inventory.find(item) != m_Inventory.end(); + } private: /** * The buy scalar. @@ -94,6 +97,10 @@ private: * The list of items the vendor sells. */ std::map m_Inventory; + + bool m_DirtyVendor; + bool m_HasStandardCostItems; + bool m_HasMultiCostItems; }; #endif // VENDORCOMPONENT_H diff --git a/dGame/dEntity/Entity.cpp b/dGame/dEntity/Entity.cpp index f638009e..2c734195 100644 --- a/dGame/dEntity/Entity.cpp +++ b/dGame/dEntity/Entity.cpp @@ -70,6 +70,7 @@ #include "RacingStatsComponent.h" #include "MinigameControlComponent.h" #include "ItemComponent.h" +#include "DonationVendorComponent.h" // Table includes #include "CDComponentsRegistryTable.h" @@ -469,6 +470,8 @@ void Entity::Initialize() { case eReplicaComponentType::IGNORE_LIST: case eReplicaComponentType::INTERACTION_MANAGER: case eReplicaComponentType::DONATION_VENDOR: + AddComponent(); + break; case eReplicaComponentType::COMBAT_MEDIATOR: case eReplicaComponentType::ACHIEVEMENT_VENDOR: case eReplicaComponentType::GATE_RUSH_CONTROL: