Skip to content

Commit 4f17454

Browse files
Nyeriahclaude
andauthored
fix(Core/Groups): preserve BoP trade window when mailing roll-won items (azerothcore#26023)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 46f0d40 commit 4f17454

2 files changed

Lines changed: 62 additions & 4 deletions

File tree

src/server/game/Entities/Player/PlayerStorage.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6209,6 +6209,32 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
62096209
return nullptr;
62106210
}
62116211

6212+
// Rehydrate looters for BoP-tradeable mail items; only the LFG mail path writes this flag, gated on the same config.
6213+
if (item->IsBOPTradable() && sWorld->getBoolConfig(CONFIG_SET_BOP_ITEM_TRADEABLE))
6214+
{
6215+
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE);
6216+
stmt->SetData(0, item->GetGUID().GetCounter());
6217+
6218+
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
6219+
{
6220+
AllowedLooterSet looters;
6221+
for (std::string_view guidStr : Acore::Tokenize((*result)[0].Get<std::string_view>(), ' ', false))
6222+
{
6223+
if (Optional<ObjectGuid::LowType> guid = Acore::StringTo<ObjectGuid::LowType>(guidStr))
6224+
looters.insert(ObjectGuid::Create<HighGuid::Player>(*guid));
6225+
else
6226+
LOG_WARN("entities.player.loading", "Player::_LoadMailedItem: invalid item_soulbound_trade_data GUID '{}' for item {}. Skipped.", guidStr, item->GetGUID().ToString());
6227+
}
6228+
6229+
if (looters.size() > 1 && proto->GetMaxStackSize() == 1 && item->IsSoulBound())
6230+
item->SetSoulboundTradeable(looters);
6231+
else
6232+
item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE);
6233+
}
6234+
else
6235+
item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE);
6236+
}
6237+
62126238
if (mail)
62136239
{
62146240
mail->AddItem(itemGuid, itemEntry);

src/server/game/Groups/Group.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,40 @@ Loot* Roll::getLoot()
6363
return getTarget();
6464
}
6565

66+
static void SendRollWonItemViaMail(Player* player, LootItem const* lootItem, uint32 itemId)
67+
{
68+
Item* mailItem = Item::CreateItem(itemId, lootItem->count, player, false, lootItem->randomPropertyId);
69+
if (!mailItem)
70+
return;
71+
72+
AllowedLooterSet looters = lootItem->GetAllowedLooters();
73+
ItemTemplate const* proto = mailItem->GetTemplate();
74+
// Preserve the 2-hour group trade window the item would have had if stored directly.
75+
if (looters.size() > 1 && proto->GetMaxStackSize() == 1 &&
76+
(proto->Bonding == BIND_WHEN_PICKED_UP || proto->Bonding == BIND_QUEST_ITEM) &&
77+
sWorld->getBoolConfig(CONFIG_SET_BOP_ITEM_TRADEABLE))
78+
{
79+
mailItem->SetBinding(true);
80+
mailItem->SetSoulboundTradeable(looters);
81+
mailItem->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, player->GetTotalPlayedTime());
82+
83+
std::string lootersStr;
84+
for (ObjectGuid const& guid : looters)
85+
{
86+
if (!lootersStr.empty())
87+
lootersStr += ' ';
88+
lootersStr += std::to_string(guid.GetCounter());
89+
}
90+
91+
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE);
92+
stmt->SetData(0, mailItem->GetGUID().GetCounter());
93+
stmt->SetData(1, lootersStr);
94+
CharacterDatabase.Execute(stmt);
95+
}
96+
97+
player->SendItemRetrievalMail(mailItem);
98+
}
99+
66100
Group::Group() : m_leaderName(""), m_groupType(GROUPTYPE_NORMAL),
67101
m_dungeonDifficulty(DUNGEON_DIFFICULTY_NORMAL), m_raidDifficulty(RAID_DIFFICULTY_10MAN_NORMAL),
68102
m_bfGroup(nullptr), m_bgGroup(nullptr), m_lootMethod(FREE_FOR_ALL), m_lootThreshold(ITEM_QUALITY_UNCOMMON),
@@ -1497,8 +1531,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
14971531
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
14981532
roll->getLoot()->unlootedCount--;
14991533
player->SendEquipError(msg, nullptr, nullptr, roll->itemid);
1500-
if (Item* mailItem = Item::CreateItem(roll->itemid, item->count, player, false, item->randomPropertyId))
1501-
player->SendItemRetrievalMail(mailItem);
1534+
SendRollWonItemViaMail(player, item, roll->itemid);
15021535
}
15031536
else
15041537
{
@@ -1580,8 +1613,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
15801613
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
15811614
roll->getLoot()->unlootedCount--;
15821615
player->SendEquipError(msg, nullptr, nullptr, roll->itemid);
1583-
if (Item* mailItem = Item::CreateItem(roll->itemid, item->count, player, false, item->randomPropertyId))
1584-
player->SendItemRetrievalMail(mailItem);
1616+
SendRollWonItemViaMail(player, item, roll->itemid);
15851617
}
15861618
else
15871619
{

0 commit comments

Comments
 (0)