diff --git a/dGame/TradingManager.cpp b/dGame/TradingManager.cpp index c13edbd84..938f380c0 100644 --- a/dGame/TradingManager.cpp +++ b/dGame/TradingManager.cpp @@ -189,7 +189,8 @@ void Trade::Complete() { recordCoins(m_CoinsA, characterA->GetID(), characterB->GetID()); recordCoins(m_CoinsB, characterB->GetID(), characterA->GetID()); - const auto giveItems = [zone](const std::vector& items, InventoryComponent* from, MissionComponent* fromMissions, + const auto tradeId = m_TradeId; + const auto giveItems = [zone, tradeId](const std::vector& items, InventoryComponent* from, MissionComponent* fromMissions, InventoryComponent* to, const LWOOBJID fromCharacter, const LWOOBJID toCharacter) { for (const auto& tradeItem : items) { auto* itemToRemove = from->FindItemById(tradeItem.itemId); @@ -204,9 +205,12 @@ void Trade::Complete() { const auto bound = itemToRemove->GetBound(); itemToRemove->SetCount(itemToRemove->GetCount() - tradeItem.itemCount); fromMissions->Progress(eMissionTaskType::GATHER, tradeItem.itemLot, LWOOBJID_EMPTY, "", -static_cast(tradeItem.itemCount)); + // Live sent the trade's ID with the traded items + ReceiveItemOptions options{}; + options.metrics.transactionId = tradeId; const auto received = whole - ? to->ReceiveItem(LWOOBJID_EMPTY, tradeItem.itemLot, tradeItem.itemCount, eLootSourceType::TRADE, config, subKey, bound) - : to->ReceiveItem(LWOOBJID_EMPTY, tradeItem.itemLot, tradeItem.itemCount, eLootSourceType::TRADE); + ? to->ReceiveItem(LWOOBJID_EMPTY, tradeItem.itemLot, tradeItem.itemCount, eLootSourceType::TRADE, config, subKey, bound, options) + : to->ReceiveItem(LWOOBJID_EMPTY, tradeItem.itemLot, tradeItem.itemCount, eLootSourceType::TRADE, {}, LWOOBJID_EMPTY, false, options); EconomyLedger::RecordTransfer({ .method = IEconomyLedger::eTransferMethod::TRADE, .itemId = tradeItem.itemId, .newItemId = received.id, .lot = tradeItem.itemLot, .count = tradeItem.itemCount, .fromCharacter = fromCharacter, .toCharacter = toCharacter, .zone = zone, diff --git a/dGame/dComponents/VendorComponent.cpp b/dGame/dComponents/VendorComponent.cpp index 131594019..554e7b2eb 100644 --- a/dGame/dComponents/VendorComponent.cpp +++ b/dGame/dComponents/VendorComponent.cpp @@ -208,7 +208,11 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) { } character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR, CoinSource::Object(*m_Parent)); - inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR); + // Live sent the vendor's LOT, and the coins paid when there were any, with the bought items + LootMetrics metrics{}; + metrics.sourceLot = m_Parent->GetLOT(); + if (coinCost > 0) metrics.currencyDelta = -static_cast(coinCost); + inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 0, false, -1, metrics); SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS); } diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index c9e3e2c96..8f5a6a9ef 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -510,6 +510,11 @@ void Mission::YieldRewards() { } } + // Live sent the mission's ID and the player's LOT with each reward item (225 of 225 live mission and achievement rewards) + LootMetrics rewardMetrics{}; + rewardMetrics.sourceLot = entity->GetLOT(); + rewardMetrics.missionId = static_cast(info.id); + if (m_Completions > 0) { std::vector> items; @@ -528,7 +533,7 @@ void Mission::YieldRewards() { // If a mission rewards zero of an item, make it reward 1. auto count = pair.second > 0 ? pair.second : 1; LOG("Player %llu is receiving %i of item %i from repeatable mission %i", entity->GetObjectID(), count, pair.first, info.id); - inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT); + inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 0, false, -1, rewardMetrics); } if (info.reward_currency_repeatable > 0 || coinsToSend > 0) { @@ -556,7 +561,7 @@ void Mission::YieldRewards() { // If a mission rewards zero of an item, make it reward 1. auto count = pair.second > 0 ? pair.second : 1; LOG("Player %llu is receiving %i of item %i from mission %i", entity->GetObjectID(), count, pair.first, info.id); - inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT); + inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 0, false, -1, rewardMetrics); } if (info.reward_currency > 0 || coinsToSend > 0) { diff --git a/dGame/dUtilities/Loot.cpp b/dGame/dUtilities/Loot.cpp index 20549b401..78e340cfc 100644 --- a/dGame/dUtilities/Loot.cpp +++ b/dGame/dUtilities/Loot.cpp @@ -523,14 +523,22 @@ Loot::Return Loot::RollLootMatrix(Entity* player, uint32_t matrixIndex) { } void Loot::GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType) { + GiveLoot(player, matrixIndex, lootSourceType, LootMetrics{}); +} + +void Loot::GiveLoot(Entity* player, const Loot::Return& result, eLootSourceType lootSourceType) { + GiveLoot(player, result, lootSourceType, LootMetrics{}); +} + +void Loot::GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType, const LootMetrics& metrics) { player = player->GetOwner(); // If the owner is overwritten, we collect that here const auto result = RollLootMatrix(player, matrixIndex); - GiveLoot(player, result, lootSourceType); + GiveLoot(player, result, lootSourceType, metrics); } -void Loot::GiveLoot(Entity* player, const Loot::Return& result, eLootSourceType lootSourceType) { +void Loot::GiveLoot(Entity* player, const Loot::Return& result, eLootSourceType lootSourceType, const LootMetrics& metrics) { player = player->GetOwner(); // if the owner is overwritten, we collect that here auto* inventoryComponent = player->GetComponent(); @@ -539,7 +547,7 @@ void Loot::GiveLoot(Entity* player, const Loot::Return& result, eLootSourceType return; for (const auto& pair : result) { - inventoryComponent->AddItem(pair.first, pair.second, lootSourceType); + inventoryComponent->AddItem(pair.first, pair.second, lootSourceType, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 0, false, -1, metrics); } } @@ -577,7 +585,11 @@ void Loot::GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t acti const auto [minCoins, maxCoins] = GetActivityCoinRange(*selectedReward); - GiveLoot(player, selectedReward->LootMatrixIndex, eLootSourceType::ACTIVITY); + // Live sent the activity's ID and the activity object's LOT with each reward item + LootMetrics metrics{}; + metrics.activityId = static_cast(activityID); + if (const auto* const sourceEntity = Game::entityManager->GetEntity(source)) metrics.sourceLot = sourceEntity->GetLOT(); + GiveLoot(player, selectedReward->LootMatrixIndex, eLootSourceType::ACTIVITY, metrics); uint32_t coins = static_cast(minCoins + GeneralUtils::GenerateRandomNumber(0, 1) * (maxCoins - minCoins)); diff --git a/dGame/dUtilities/Loot.h b/dGame/dUtilities/Loot.h index c2eba6e4a..842fcec05 100644 --- a/dGame/dUtilities/Loot.h +++ b/dGame/dUtilities/Loot.h @@ -8,6 +8,7 @@ #include class Entity; +struct LootMetrics; struct CDActivityRewards; struct CDCurrencyTable; @@ -31,6 +32,9 @@ namespace Loot { void CacheMatrix(const uint32_t matrixIndex); void GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType = eLootSourceType::NONE); void GiveLoot(Entity* player, const Loot::Return& result, eLootSourceType lootSourceType = eLootSourceType::NONE); + // As above, telling the client where the items came from (LootMetrics) + void GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType, const LootMetrics& metrics); + void GiveLoot(Entity* player, const Loot::Return& result, eLootSourceType lootSourceType, const LootMetrics& metrics); void GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating = 0); void DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins); void DropItem(Entity& player, GameMessages::DropClientLoot& lootMsg, bool useTeam = false, bool forceFfa = false); diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index e258824aa..fbde91cd7 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -239,7 +239,10 @@ namespace Mail { LwoNameValue config; config.InsertLines(playerMail->itemConfig); - const auto claimed = inv->ReceiveItem(playerMail->itemID, playerMail->itemLOT, playerMail->itemCount, eLootSourceType::MAIL, config, playerMail->itemSubkey); + // Live sent the mail's ID with the attachment + ReceiveItemOptions options{}; + options.metrics.mailId = static_cast(mailID); + const auto claimed = inv->ReceiveItem(playerMail->itemID, playerMail->itemLOT, playerMail->itemCount, eLootSourceType::MAIL, config, playerMail->itemSubkey, false, options); Database::Get()->ClaimMailItem(mailID); DashboardNotify::Changed("mail", playerMail->receiverId);