Files
DarkflameServer/dGame/dComponents/Vendor/AchievementVendorComponent.cpp
Aaron Kimbrell 9ffed42772 fix: limit the count of a vendor purchase and work its price out in 64 bits
A purchase took any count, and the coin, alternate currency and
commendation prices were worked out as 32 bit numbers that wrap for a
large count. A purchase is now for 1 to 1000 items and the prices are
64 bit numbers.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 16:04:52 -06:00

106 lines
4.6 KiB
C++

#include "AchievementVendorComponent.h"
#include "MissionComponent.h"
#include "InventoryComponent.h"
#include "eMissionState.h"
#include "CDComponentsRegistryTable.h"
#include "CDItemComponentTable.h"
#include "eVendorTransactionResult.h"
#include "CheatDetection.h"
#include "Logger.h"
#include "Game.h"
#include "VendorPricing.h"
#include "UserManager.h"
#include "CDMissionsTable.h"
#include "Entity.h"
#include "ObjectLoadState.h"
#include "ProximityMonitorComponent.h"
AchievementVendorComponent::AchievementVendorComponent(Entity* parent, const int32_t componentID) : BaseVendorComponent(parent, componentID) {
}
// The base vendor's template load, then the achievement vendor's stock
void AchievementVendorComponent::LoadDataFromTemplate() {
BaseVendorComponent::LoadDataFromTemplate();
RefreshInventory(true);
};
bool AchievementVendorComponent::SellsItem(Entity* buyer, const LOT lot) {
auto* missionComponent = buyer->GetComponent<MissionComponent>();
if (!missionComponent) return false;
if (m_PlayerPurchasableItems[buyer->GetObjectID()].contains(lot)) {
return true;
}
CDMissionsTable* missionsTable = CDClientManager::GetTable<CDMissionsTable>();
const auto missions = missionsTable->GetMissionsForReward(lot);
for (const auto mission : missions) {
if (missionComponent->GetMissionState(mission) == eMissionState::COMPLETE) {
m_PlayerPurchasableItems[buyer->GetObjectID()].insert(lot);
return true;
}
}
return false;
}
void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
if (!VendorPricing::IsValidBuyCount(count)) {
LOG("Player %llu tried to buy %u of item %i from vendor %i", buyer->GetObjectID(), count, lot, m_Parent->GetLOT());
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
// get the item Comp from the item LOT
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
int itemCompID = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::ITEM);
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
uint32_t costLOT = itemComp.commendationLOT;
if (costLOT == -1 || !SellsItem(buyer, lot)) {
auto* user = UserManager::Instance()->GetUser(buyer->GetSystemAddress());
CheatDetection::ReportCheat(user, buyer->GetSystemAddress(), "Attempted to buy item %i from achievement vendor %i that is not purchasable", lot, m_Parent->GetLOT());
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
auto* inventoryComponent = buyer->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
if (costLOT == 13763) { // Faction Token Proxy
auto* missionComponent = buyer->GetComponent<MissionComponent>();
if (!missionComponent) return;
if (missionComponent->GetMissionState(545) == eMissionState::COMPLETE) costLOT = 8318; // "Assembly Token"
if (missionComponent->GetMissionState(556) == eMissionState::COMPLETE) costLOT = 8321; // "Venture League Token"
if (missionComponent->GetMissionState(567) == eMissionState::COMPLETE) costLOT = 8319; // "Sentinels Token"
if (missionComponent->GetMissionState(578) == eMissionState::COMPLETE) costLOT = 8320; // "Paradox Token"
}
const uint64_t altCurrencyCost = static_cast<uint64_t>(itemComp.commendationCost) * count;
if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost || !inventoryComponent->RemoveItem(costLOT, static_cast<uint32_t>(altCurrencyCost), eInventoryType::ALL)) {
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
}
void AchievementVendorComponent::RefreshInventory(bool isCreation) {
SetHasStandardCostItems(true);
Game::entityManager->SerializeEntity(m_Parent);
}
void AchievementVendorComponent::Allocator(Entity& entity, ObjectLoadState& state) {
const auto achievementVendorID = state.GetComponentID(eReplicaComponentType::ACHIEVEMENT_VENDOR, -1);
if (achievementVendorID > -1) {
if (!entity.HasComponent(eReplicaComponentType::PROXIMITY_MONITOR)) entity.AddComponent<ProximityMonitorComponent>(-1);
entity.AddComponent<AchievementVendorComponent>(achievementVendorID);
}
}