Skip to content

Commit

Permalink
feat: achievement vendor and vendor feedback (#1461)
Browse files Browse the repository at this point in the history
* Groundwork

* movie buying logic out of gm handler
make transaction result more useful

* Full implementation
Cleanup and fix some calls in gamemessages

* Load the component in the entity
Patch Auth

* new line at eof

* cache lookups

* remove sort

* fix includes
  • Loading branch information
aronwk-aaron authored Feb 25, 2024
1 parent 1328850 commit e729c7f
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 95 deletions.
2 changes: 1 addition & 1 deletion dCommon/dEnums/eReplicaComponentType.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ enum class eReplicaComponentType : uint32_t {
INTERACTION_MANAGER,
DONATION_VENDOR,
COMBAT_MEDIATOR,
COMMENDATION_VENDOR,
ACHIEVEMENT_VENDOR,
GATE_RUSH_CONTROL,
RAIL_ACTIVATOR,
ROLLER,
Expand Down
15 changes: 15 additions & 0 deletions dCommon/dEnums/eVendorTransactionResult.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef __EVENDORTRANSACTIONRESULT__
#define __EVENDORTRANSACTIONRESULT__

#include <cstdint>

enum class eVendorTransactionResult : uint32_t {
SELL_SUCCESS = 0,
SELL_FAIL,
PURCHASE_SUCCESS,
PURCHASE_FAIL,
DONATION_FAIL,
DONATION_FULL
};

#endif // !__EVENDORTRANSACTIONRESULT__
10 changes: 9 additions & 1 deletion dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ void CDMissionsTable::LoadValuesFromDatabase() {
entries.push_back(entry);
tableData.nextRow();
}

tableData.finalize();

Default.id = -1;
Expand Down Expand Up @@ -118,3 +117,12 @@ const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& foun
return Default;
}

const std::set<int32_t> CDMissionsTable::GetMissionsForReward(LOT lot) {
std::set<int32_t> toReturn {};
for (const auto& entry : GetEntries()) {
if (lot == entry.reward_item1 || lot == entry.reward_item2 || lot == entry.reward_item3 || lot == entry.reward_item4) {
toReturn.insert(entry.id);
}
}
return toReturn;
}
3 changes: 3 additions & 0 deletions dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class CDMissionsTable : public CDTable<CDMissionsTable, std::vector<CDMissions>>

const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const;

const std::set<int32_t> GetMissionsForReward(LOT lot);


static CDMissions Default;
};

8 changes: 8 additions & 0 deletions dGame/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
#include "CollectibleComponent.h"
#include "ItemComponent.h"
#include "GhostComponent.h"
#include "AchievementVendorComponent.h"

// Table includes
#include "CDComponentsRegistryTable.h"
Expand Down Expand Up @@ -615,6 +616,8 @@ void Entity::Initialize() {
AddComponent<VendorComponent>();
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) {
AddComponent<DonationVendorComponent>();
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ACHIEVEMENT_VENDOR, -1) != -1)) {
AddComponent<AchievementVendorComponent>();
}

if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) {
Expand Down Expand Up @@ -1191,6 +1194,11 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
}

AchievementVendorComponent* achievementVendorComponent;
if (TryGetComponent(eReplicaComponentType::ACHIEVEMENT_VENDOR, achievementVendorComponent)) {
achievementVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
}

BouncerComponent* bouncerComponent;
if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) {
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate);
Expand Down
72 changes: 72 additions & 0 deletions dGame/dComponents/AchievementVendorComponent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#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 "UserManager.h"
#include "CDMissionsTable.h"

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) {
// 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());
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}

auto* inventoryComponent = buyer->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
GameMessages::SendVendorTransactionResult(buyer, 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 uint32_t altCurrencyCost = itemComp.commendationCost * count;
if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}

inventoryComponent->RemoveItem(costLOT, altCurrencyCost);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);

}
23 changes: 23 additions & 0 deletions dGame/dComponents/AchievementVendorComponent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef __ACHIEVEMENTVENDORCOMPONENT__H__
#define __ACHIEVEMENTVENDORCOMPONENT__H__

#include "VendorComponent.h"
#include "eReplicaComponentType.h"
#include <set>
#include <map>

class Entity;

class AchievementVendorComponent final : public VendorComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {};
bool SellsItem(Entity* buyer, const LOT lot);
void Buy(Entity* buyer, LOT lot, uint32_t count);

private:
std::map<LWOOBJID,std::set<LOT>> m_PlayerPurchasableItems;
};


#endif //!__ACHIEVEMENTVENDORCOMPONENT__H__
1 change: 1 addition & 0 deletions dGame/dComponents/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(DGAME_DCOMPONENTS_SOURCES
"AchievementVendorComponent.cpp"
"ActivityComponent.cpp"
"BaseCombatAIComponent.cpp"
"BouncerComponent.cpp"
Expand Down
62 changes: 62 additions & 0 deletions dGame/dComponents/VendorComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
#include "CDLootMatrixTable.h"
#include "CDLootTableTable.h"
#include "CDItemComponentTable.h"
#include "InventoryComponent.h"
#include "Character.h"
#include "eVendorTransactionResult.h"
#include "UserManager.h"
#include "CheatDetection.h"

VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
m_HasStandardCostItems = false;
Expand Down Expand Up @@ -151,3 +156,60 @@ void VendorComponent::HandleMrReeCameras(){
m_Inventory.push_back(SoldItem(camera, 0));
}
}


void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {

if (!SellsItem(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());
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}

auto* inventoryComponent = buyer->GetComponent<InventoryComponent>();
if (!inventoryComponent) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
int itemCompID = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::ITEM);
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);

// Extra currency that needs to be deducted in case of crafting
auto craftingCurrencies = CDItemComponentTable::ParseCraftingCurrencies(itemComp);
for (const auto& [crafintCurrencyLOT, crafintCurrencyCount]: craftingCurrencies) {
if (inventoryComponent->GetLotCount(crafintCurrencyLOT) < (crafintCurrencyCount * count)) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
}
for (const auto& [crafintCurrencyLOT, crafintCurrencyCount]: craftingCurrencies) {
inventoryComponent->RemoveItem(crafintCurrencyLOT, crafintCurrencyCount * count);
}


float buyScalar = GetBuyScalar();
const auto coinCost = static_cast<uint32_t>(std::floor((itemComp.baseValue * buyScalar) * count));

Character* character = buyer->GetCharacter();
if (!character || character->GetCoins() < coinCost) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}

if (Inventory::IsValidItem(itemComp.currencyLOT)) {
const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * buyScalar) * count;
if (inventoryComponent->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
inventoryComponent->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
}

character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);

}
1 change: 1 addition & 0 deletions dGame/dComponents/VendorComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class VendorComponent : public Component {
m_DirtyVendor = true;
}

void Buy(Entity* buyer, LOT lot, uint32_t count);

private:
void SetupMaxCustomVendor();
Expand Down
Loading

0 comments on commit e729c7f

Please sign in to comment.