account bank

This commit is contained in:
luis
2026-05-15 23:31:13 -03:00
parent c5e496fc22
commit 6d32e75543
15 changed files with 661 additions and 36 deletions
+24 -2
View File
@@ -1344,7 +1344,7 @@ bool Item::CanBeTraded(bool mail, bool trade) const
if (m_lootGenerated)
return false;
if ((!mail || !IsBoundAccountWide()) && (IsSoulBound() && (!IsBOPTradeable() || !trade)))
if ((!mail || !IsAccountBound()) && (IsSoulBound() && (!IsBOPTradeable() || !trade)))
return false;
if (IsBag() && (Player::IsBagPos(GetPos()) || !ToBag()->IsEmpty()))
@@ -1781,7 +1781,7 @@ bool Item::IsBindedNotWith(Player const* player) const
return false;
// BOA item case
if (IsBoundAccountWide())
if (IsAccountBound())
return false;
return true;
@@ -3187,3 +3187,25 @@ void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values)
break;
}
}
bool Item::IsWarbandBound() const
{
ItemBondingType bonding = GetBonding();
if (bonding == BIND_WOW_ACCOUNT || bonding == BIND_BNET_ACCOUNT)
return true;
if (bonding == BIND_BNET_ACCOUNT_UNTIL_EQUIPPED && !HasItemFlag(ITEM_FIELD_FLAG_CONVERTED_WARBOUND))
return true;
return false;
}
void Item::ConvertToSoulbound()
{
if (GetBonding() != BIND_BNET_ACCOUNT_UNTIL_EQUIPPED)
return;
if (HasItemFlag(ITEM_FIELD_FLAG_CONVERTED_WARBOUND))
return;
SetBinding(true);
SetItemFlag(ITEM_FIELD_FLAG_CONVERTED_WARBOUND);
SetState(ITEM_CHANGED, GetOwner());
}
+5
View File
@@ -227,6 +227,11 @@ class TC_GAME_API Item : public Object
bool IsSoulBound() const { return HasItemFlag(ITEM_FIELD_FLAG_SOULBOUND); }
bool IsBoundAccountWide() const { return GetTemplate()->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT); }
bool IsBattlenetAccountBound() const { return GetTemplate()->HasFlag(ITEM_FLAG2_BNET_ACCOUNT_TRADE_OK); }
//WowCommunity
bool IsWarbandBound() const;
bool IsAccountBound() const { return IsBoundAccountWide() || IsWarbandBound(); }
void ConvertToSoulbound();
//WowCommunity
bool IsBindedNotWith(Player const* player) const;
bool IsBoundByEnchant() const;
virtual void SaveToDB(CharacterDatabaseTransaction trans);
+4 -4
View File
@@ -131,9 +131,9 @@ enum ItemBondingType
BIND_QUEST = 4,
BIND_UNUSED_1 = 5,
BIND_UNUSED_2 = 6,
BIND_WOW_ACCOUNT = 7,
BIND_BNET_ACCOUNT = 8,
BIND_BNET_ACCOUNT_UNTIL_EQUIPPED = 9,
BIND_WOW_ACCOUNT = 7,// Bind to Warband (WoW account scope)
BIND_BNET_ACCOUNT = 8,// Bind to Warband (Battle.net account scope)
BIND_BNET_ACCOUNT_UNTIL_EQUIPPED = 9,// Bind to Warband until equipped, then becomes soulbound
};
/* /// @todo: Requiring actual cases in which using (an) item isn't allowed while shapeshifted. Else, this flag would need an implementation.
@@ -146,7 +146,7 @@ enum ItemFieldFlags : uint32
ITEM_FIELD_FLAG_TRANSLATED = 0x00000002, // Item text will not read as garbage when player does not know the language
ITEM_FIELD_FLAG_UNLOCKED = 0x00000004, // Item had lock but can be opened now
ITEM_FIELD_FLAG_WRAPPED = 0x00000008, // Item is wrapped and contains another item
ITEM_FIELD_FLAG_UNK2 = 0x00000010,
ITEM_FIELD_FLAG_CONVERTED_WARBOUND = 0x00000010, // BtWuE item has been equipped and converted to soulbound
ITEM_FIELD_FLAG_UNK3 = 0x00000020,
ITEM_FIELD_FLAG_UNK4 = 0x00000040,
ITEM_FIELD_FLAG_UNK5 = 0x00000080,
+331 -12
View File
@@ -9990,6 +9990,8 @@ bool Player::IsEquipmentPos(uint8 bag, uint8 slot)
return true;
if (bag == INVENTORY_SLOT_BAG_0 && (slot >= REAGENT_BAG_SLOT_START && slot < REAGENT_BAG_SLOT_END))
return true;
if (bag == INVENTORY_SLOT_BAG_0 && (slot >= ACCOUNT_BANK_SLOT_BAG_START && slot < ACCOUNT_BANK_SLOT_BAG_END))
return true;
return false;
}
@@ -10020,10 +10022,12 @@ bool Player::IsChildEquipmentPos(uint8 bag, uint8 slot)
return bag == INVENTORY_SLOT_BAG_0 && (slot >= CHILD_EQUIPMENT_SLOT_START && slot < CHILD_EQUIPMENT_SLOT_END);
}
bool Player::IsAccountBankPos(uint8 bag, uint8 /*slot*/)
bool Player::IsAccountBankPos(uint8 bag, uint8 slot)
{
if (bag >= ACCOUNT_BANK_SLOT_BAG_START && bag < ACCOUNT_BANK_SLOT_BAG_END)
return true;
if (bag == INVENTORY_SLOT_BAG_0 && slot >= ACCOUNT_BANK_SLOT_BAG_START && slot < ACCOUNT_BANK_SLOT_BAG_END)
return true;
return false;
}
@@ -10063,11 +10067,16 @@ bool Player::IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const
if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
return true;
// account bank bag slots
if (slot >= ACCOUNT_BANK_SLOT_BAG_START && slot < ACCOUNT_BANK_SLOT_BAG_END)
return true;
return false;
}
// bag content slots
// bank bag content slots
// account bank bag content slots
if (Bag* pBag = GetBagByPos(bag))
{
// any post selected
@@ -11719,6 +11728,9 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
if (pItem->GetBonding() == BIND_ON_ACQUIRE ||
pItem->GetBonding() == BIND_QUEST ||
pItem->GetBonding() == BIND_WOW_ACCOUNT ||
pItem->GetBonding() == BIND_BNET_ACCOUNT ||
pItem->GetBonding() == BIND_BNET_ACCOUNT_UNTIL_EQUIPPED ||
(pItem->GetBonding() == BIND_ON_EQUIP && IsBagPos(pos)))
pItem->SetBinding(true);
@@ -11758,6 +11770,9 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
{
if (pItem2->GetBonding() == BIND_ON_ACQUIRE ||
pItem2->GetBonding() == BIND_QUEST ||
pItem2->GetBonding() == BIND_WOW_ACCOUNT ||
pItem2->GetBonding() == BIND_BNET_ACCOUNT ||
pItem2->GetBonding() == BIND_BNET_ACCOUNT_UNTIL_EQUIPPED ||
(pItem2->GetBonding() == BIND_ON_EQUIP && IsBagPos(pos)))
pItem2->SetBinding(true);
@@ -12180,9 +12195,13 @@ void Player::VisualizeItem(uint8 slot, Item* pItem)
return;
// check also BIND_ON_ACQUIRE and BIND_QUEST for .additem or .additemset case by GM (not binded at adding to inventory)
if (pItem->GetBonding() == BIND_ON_EQUIP || pItem->GetBonding() == BIND_ON_ACQUIRE || pItem->GetBonding() == BIND_QUEST)
if (pItem->GetBonding() == BIND_ON_EQUIP || pItem->GetBonding() == BIND_ON_ACQUIRE || pItem->GetBonding() == BIND_QUEST
|| pItem->GetBonding() == BIND_WOW_ACCOUNT || pItem->GetBonding() == BIND_BNET_ACCOUNT
|| pItem->GetBonding() == BIND_BNET_ACCOUNT_UNTIL_EQUIPPED)
{
pItem->SetBinding(true);
if (pItem->GetBonding() == BIND_BNET_ACCOUNT_UNTIL_EQUIPPED)
pItem->ConvertToSoulbound();
if (IsInWorld())
GetSession()->GetCollectionMgr()->AddItemAppearance(pItem);
}
@@ -18833,6 +18852,13 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
if (!m_taxi.LoadTaxiMask(fields.taximask)) // must be before InitTaxiNodesForLevel
TC_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player ({}) has invalid taximask ({}) in DB. Forced partial load.", GetGUID().ToString(), fields.taximask);
//WowCommunity
if (PreparedQueryResult warbandTaxiResult = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WARBAND_TAXI_MASK))
{
TaxiMask accountMask = PlayerTaxi::LoadTaxiMaskFromString((*warbandTaxiResult)[0].GetString());
m_taxi.MergeAccountTaxiMask(accountMask);
}
//WowCommunity
uint32 extraflags = fields.extra_flags;
_LoadPetStable(fields.summonedPetNumber, holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS));
@@ -18930,6 +18956,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
_LoadCharacterBankTabSettings(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANK_TAB_SETTINGS));
_LoadAccountBankTabSettings(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_BANK_TAB_SETTINGS));
_LoadInventory(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY),
holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ARTIFACTS),
@@ -18939,6 +18966,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AZERITE_EMPOWERED),
time_diff);
_LoadAccountBankItems(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_BANK_ITEMS), time_diff);
// update items with duration and realtime
UpdateItemDuration(time_diff, true);
@@ -21458,6 +21487,19 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
_SaveCUFProfiles(trans);
_SavePlayerData(trans);
_SaveCharacterBankTabSettings(trans);
//WowCommunity
_SaveAccountBankTabSettings(trans);
_SaveAccountBankItems(trans);
{
std::ostringstream ss;
ss << m_taxi;
CharacterDatabasePreparedStatement* taxiStmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_WARBAND_TAXI_MASK);
taxiStmt->setUInt32(0, GetSession()->GetBattlenetAccountId());
taxiStmt->setString(1, ss.str());
trans->Append(taxiStmt);
}
//WowCommunity
for (auto const& [type, garrison] : _garrisons)
garrison->SaveToDB(trans);
for (auto const& h : _housings)
@@ -21776,21 +21818,31 @@ void Player::_SaveInventory(CharacterDatabaseTransaction trans)
}
}
// Account bank items are saved separately in _SaveAccountBankItems ? skip inventory position save
bool isAccountBankItem = IsAccountBankPos(item->GetBagSlot(), item->GetSlot())
|| (container && IsAccountBankPos(INVENTORY_SLOT_BAG_0, container->GetSlot()));
switch (item->GetState())
{
case ITEM_NEW:
case ITEM_CHANGED:
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_INVENTORY_ITEM);
stmt->setUInt64(0, GetGUID().GetCounter());
stmt->setUInt64(1, container ? container->GetGUID().GetCounter() : UI64LIT(0));
stmt->setUInt8 (2, item->GetSlot());
stmt->setUInt64(3, item->GetGUID().GetCounter());
trans->Append(stmt);
if (!isAccountBankItem)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_INVENTORY_ITEM);
stmt->setUInt64(0, GetGUID().GetCounter());
stmt->setUInt64(1, container ? container->GetGUID().GetCounter() : UI64LIT(0));
stmt->setUInt8(2, item->GetSlot());
stmt->setUInt64(3, item->GetGUID().GetCounter());
trans->Append(stmt);
}
break;
case ITEM_REMOVED:
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->setUInt64(0, item->GetGUID().GetCounter());
trans->Append(stmt);
if (!isAccountBankItem)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->setUInt64(0, item->GetGUID().GetCounter());
trans->Append(stmt);
}
break;
case ITEM_UNCHANGED:
break;
@@ -32867,7 +32919,9 @@ bool Player::ProcessItemCast(SpellCastRequest& castRequest, SpellCastTargets con
}
// check also BIND_ON_ACQUIRE and BIND_QUEST for .additem or .additemset case by GM (not binded at adding to inventory)
if (item->GetBonding() == BIND_ON_USE || item->GetBonding() == BIND_ON_ACQUIRE || item->GetBonding() == BIND_QUEST)
if (item->GetBonding() == BIND_ON_USE || item->GetBonding() == BIND_ON_ACQUIRE || item->GetBonding() == BIND_QUEST
|| item->GetBonding() == BIND_WOW_ACCOUNT || item->GetBonding() == BIND_BNET_ACCOUNT
|| item->GetBonding() == BIND_BNET_ACCOUNT_UNTIL_EQUIPPED)
{
if (!item->IsSoulBound())
{
@@ -33100,3 +33154,268 @@ void Player::ResummonAnimalCompanionIfAny()
}
}
}
std::vector<Item*> Player::GetWarboundItemsToDeposit()
{
std::vector<Item*> itemList;
ForEachItem(ItemSearchLocation::Inventory, [&itemList](Item* item)
{
// Only deposit warbound/BoA items, not character-soulbound
if (item->IsAccountBound() || !item->IsSoulBound())
{
// Skip quest items and bags
if (item->GetTemplate()->GetClass() != ITEM_CLASS_QUEST && !item->IsNotEmptyBag())
itemList.push_back(item);
}
return ItemSearchCallbackResult::Continue;
});
return itemList;
}
InventoryResult Player::CanAccountBankItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap) const
{
if (!pItem)
return swap ? EQUIP_ERR_CANT_SWAP : EQUIP_ERR_ITEM_NOT_FOUND;
// Check: no character-soulbound items allowed (only warbound/BoA/unbound)
if (pItem->IsSoulBound() && !pItem->IsAccountBound())
return EQUIP_ERR_NO_SOULBOUND_ITEM_IN_ACCOUNT_BANK;
// Check: no quest items in account bank
if (pItem->GetTemplate()->GetClass() == ITEM_CLASS_QUEST)
return EQUIP_ERR_CANT_SWAP;
uint32 count = pItem->GetCount();
ItemTemplate const* pProto = pItem->GetTemplate();
// Specific slot requested
if (bag != NULL_BAG && slot != NULL_SLOT)
{
if (bag >= ACCOUNT_BANK_SLOT_BAG_START && bag < ACCOUNT_BANK_SLOT_BAG_END)
{
InventoryResult res = CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap, pItem);
if (res != EQUIP_ERR_OK)
return res;
if (count == 0)
return EQUIP_ERR_OK;
}
return EQUIP_ERR_BANK_FULL;
}
// Specific bag requested
if (bag != NULL_BAG && slot == NULL_SLOT)
{
if (bag >= ACCOUNT_BANK_SLOT_BAG_START && bag < ACCOUNT_BANK_SLOT_BAG_END)
{
// Try merge first, then empty slots
InventoryResult res = CanStoreItem_InBag(bag, dest, pProto, count, true, false, pItem, NULL_BAG, NULL_SLOT);
if (res != EQUIP_ERR_OK)
res = CanStoreItem_InBag(bag, dest, pProto, count, false, false, pItem, NULL_BAG, NULL_SLOT);
if (res != EQUIP_ERR_OK)
return res;
if (count == 0)
return EQUIP_ERR_OK;
}
return EQUIP_ERR_BANK_FULL;
}
// No specific bag/slot: search all account bank bags
// First pass: try to merge with existing stacks
for (uint8 i = ACCOUNT_BANK_SLOT_BAG_START; i < ACCOUNT_BANK_SLOT_BAG_END; i++)
{
InventoryResult res = CanStoreItem_InBag(i, dest, pProto, count, true, false, pItem, bag, slot);
if (res != EQUIP_ERR_OK)
continue;
if (count == 0)
return EQUIP_ERR_OK;
}
// Second pass: try empty slots
for (uint8 i = ACCOUNT_BANK_SLOT_BAG_START; i < ACCOUNT_BANK_SLOT_BAG_END; i++)
{
InventoryResult res = CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem, bag, slot);
if (res != EQUIP_ERR_OK)
continue;
if (count == 0)
return EQUIP_ERR_OK;
}
return EQUIP_ERR_BANK_FULL;
}
void Player::_LoadAccountBankTabSettings(PreparedQueryResult result)
{
uint8 tabCount = 0;
if (result)
{
do
{
DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (tabId)(name)(icon)(description)(depositFlags)) fields { *result };
uint8 tabId = fields.tabId().GetUInt8();
if (tabId >= (ACCOUNT_BANK_SLOT_BAG_END - ACCOUNT_BANK_SLOT_BAG_START))
continue;
SetAccountBankTabSettings(tabId, fields.name().GetString(), fields.icon().GetString(),
fields.description().GetString(), static_cast<BagSlotFlags>(fields.depositFlags().GetUInt32()));
if (tabId >= tabCount)
tabCount = tabId + 1;
} while (result->NextRow());
}
// Derive tab count from the rows loaded
SetAccountBankTabCount(tabCount);
while (m_activePlayerData->AccountBankTabSettings.size() < *m_activePlayerData->NumAccountBankTabs)
AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::AccountBankTabSettings));
}
void Player::_LoadAccountBankItems(PreparedQueryResult result, uint32 timeDiff)
{
// Same field layout as character_inventory load, but with bag/slot from account_bank_item at the end
// Fields 0-51: item_instance fields (same as _LoadInventory)
// Field 52: abi.bag (tab index 0-4)
// Field 53: abi.slot (slot within tab 0-97)
if (!result)
return;
uint32 zoneId = GetZoneId();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
m_itemUpdateQueueBlocked = true;
do
{
Field* fields = result->Fetch();
if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields))
{
uint8 tabIndex = fields[52].GetUInt8();
uint8 slot = fields[53].GetUInt8();
if (tabIndex >= GetAccountBankTabCount())
{
TC_LOG_ERROR("entities.player", "Player::_LoadAccountBankItems: Player '{}' ({}) has account bank item ({}, entry: {}) in tab {} which exceeds tab count {}. Skipping.",
GetName(), GetGUID().ToString(), item->GetGUID().ToString(), item->GetEntry(), tabIndex, GetAccountBankTabCount());
item->DeleteFromDB(trans);
delete item;
continue;
}
uint8 bagSlot = ACCOUNT_BANK_SLOT_BAG_START + tabIndex;
// Ensure the bag exists at this position
Bag* bag = GetBagByPos(bagSlot);
if (!bag)
{
// Create the bag item for this tab if it doesn't exist yet
if (Item* bagItem = Item::CreateItem(ITEM_ACCOUNT_BANK_TAB_BAG, 1, ItemContext::NONE, this))
{
uint16 bagPos = (INVENTORY_SLOT_BAG_0 << 8) | bagSlot;
bagItem->SetContainer(nullptr);
bagItem->SetSlot(bagSlot);
StoreItem(ItemPosCountVec(1, ItemPosCount(bagPos, 1)), bagItem, true);
bagItem->SetState(ITEM_UNCHANGED, this);
bag = bagItem->ToBag();
}
if (!bag)
{
TC_LOG_ERROR("entities.player", "Player::_LoadAccountBankItems: Player '{}' ({}) failed to create account bank bag for tab {}.",
GetName(), GetGUID().ToString(), tabIndex);
item->DeleteFromDB(trans);
delete item;
continue;
}
}
GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item);
GetSession()->GetCollectionMgr()->AddItemAppearance(item);
ItemPosCountVec dest;
InventoryResult err = CanStoreItem(bagSlot, slot, dest, item);
if (err == EQUIP_ERR_OK)
{
item = StoreItem(dest, item, true);
item->SetState(ITEM_UNCHANGED, this);
}
else
{
TC_LOG_ERROR("entities.player", "Player::_LoadAccountBankItems: Player '{}' ({}) has account bank item ({}, entry: {}) which can't be loaded (tab {}, slot {}) by reason {}. Item will be sent by mail.",
GetName(), GetGUID().ToString(), item->GetGUID().ToString(), item->GetEntry(), tabIndex, slot, uint32(err));
item->DeleteFromInventoryDB(trans);
MailDraft draft(GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM), "There were problems with equipping item(s).");
draft.AddItem(item);
draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
}
}
} while (result->NextRow());
m_itemUpdateQueueBlocked = false;
CharacterDatabase.CommitTransaction(trans);
}
void Player::_SaveAccountBankTabSettings(CharacterDatabaseTransaction trans) const
{
uint32 bnetAccountId = GetSession()->GetBattlenetAccountId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_BANK_TAB_SETTINGS);
stmt->setUInt32(0, bnetAccountId);
trans->Append(stmt);
for (std::size_t i = 0; i < m_activePlayerData->AccountBankTabSettings.size(); ++i)
{
UF::BankTabSettings const& tabSetting = m_activePlayerData->AccountBankTabSettings[i];
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ACCOUNT_BANK_TAB_SETTINGS);
stmt->setUInt32(0, bnetAccountId);
stmt->setUInt8(1, i);
stmt->setString(2, *tabSetting.Name);
stmt->setString(3, *tabSetting.Icon);
stmt->setString(4, *tabSetting.Description);
stmt->setInt32(5, *tabSetting.DepositFlags);
trans->Append(stmt);
}
}
void Player::_SaveAccountBankItems(CharacterDatabaseTransaction trans)
{
uint32 bnetAccountId = GetSession()->GetBattlenetAccountId();
// Delete all account bank item positions ? they will be re-inserted below
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_BANK_ITEMS_BY_BNET);
stmt->setUInt32(0, bnetAccountId);
trans->Append(stmt);
// Re-insert current positions
for (uint8 tabIndex = 0; tabIndex < GetAccountBankTabCount(); ++tabIndex)
{
uint8 bagSlot = ACCOUNT_BANK_SLOT_BAG_START + tabIndex;
Bag* bag = GetBagByPos(bagSlot);
if (!bag)
continue;
for (uint32 slot = 0; slot < bag->GetBagSize(); ++slot)
{
Item* item = bag->GetItemByPos(slot);
if (!item)
continue;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_ACCOUNT_BANK_ITEM);
stmt->setUInt32(0, bnetAccountId);
stmt->setUInt8(1, tabIndex);
stmt->setUInt8(2, slot);
stmt->setUInt64(3, item->GetGUID().GetCounter());
trans->Append(stmt);
}
}
}
+21 -2
View File
@@ -848,7 +848,7 @@ enum class ItemSearchLocation
Inventory = 0x02,
Bank = 0x04,
ReagentBank = 0x08,
AccountBank = 0x10, // NYI
AccountBank = 0x10,
Default = Equipment | Inventory,
Everywhere = Equipment | Inventory | Bank | ReagentBank
@@ -1037,6 +1037,9 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_HOUSING_ROOMS,
PLAYER_LOGIN_QUERY_LOAD_HOUSING_FIXTURES,
PLAYER_LOGIN_QUERY_LOAD_HOUSING_CATALOG,
PLAYER_LOGIN_QUERY_LOAD_WARBAND_TAXI_MASK,
PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_BANK_TAB_SETTINGS,
PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_BANK_ITEMS,
MAX_PLAYER_LOGIN_QUERY
};
@@ -1482,6 +1485,16 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
return false;
}
if (flag.HasFlag(ItemSearchLocation::AccountBank))
{
for (uint8 i = ACCOUNT_BANK_SLOT_BAG_START; i < ACCOUNT_BANK_SLOT_BAG_END; ++i)
if (Bag* bag = GetBagByPos(i))
for (uint32 j = 0; j < GetBagSize(bag); ++j)
if (Item* pItem = GetItemInBag(bag, j))
if (callback(pItem) == ItemSearchCallbackResult::Stop)
return false;
}
return true;
}
@@ -1501,6 +1514,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
Item* GetUseableItemByPos(uint8 bag, uint8 slot) const;
Bag* GetBagByPos(uint8 slot) const;
std::vector<Item*> GetCraftingReagentItemsToDeposit();
std::vector<Item*> GetWarboundItemsToDeposit();//WowCommunity
Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const;
Item* GetShield(bool useable = false) const;
Item* GetChildItemByGuid(ObjectGuid guid) const;
@@ -1515,7 +1529,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
static bool IsBankPos(uint8 bag, uint8 slot);
static bool IsChildEquipmentPos(uint16 pos) { return IsChildEquipmentPos(pos >> 8, pos & 255); }
static bool IsChildEquipmentPos(uint8 bag, uint8 slot);
static bool IsAccountBankPos(uint16 pos) { return IsBankPos(pos >> 8, pos & 255); }
static bool IsAccountBankPos(uint16 pos) { return IsAccountBankPos(pos >> 8, pos & 255); }
static bool IsAccountBankPos(uint8 bag, uint8 slot);
bool IsValidPos(uint16 pos, bool explicit_pos) const { return IsValidPos(pos >> 8, pos & 255, explicit_pos); }
bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const;
@@ -1581,6 +1595,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
InventoryResult CanUnequipItems(uint32 item, uint32 count) const;
InventoryResult CanUnequipItem(uint16 src, bool swap) const;
InventoryResult CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap, bool not_loading = true, bool reagentBankOnly = false) const;
InventoryResult CanAccountBankItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap) const;
InventoryResult CanUseItem(Item* pItem, bool not_loading = true) const;
bool HasItemTotemCategory(uint32 TotemCategory) const;
InventoryResult CanUseItem(ItemTemplate const* pItem, bool skipRequiredLevelCheck = false) const;
@@ -3237,6 +3252,8 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void _LoadCUFProfiles(PreparedQueryResult result);
void _LoadPlayerData(PreparedQueryResult elementsResult, PreparedQueryResult flagsResult);
void _LoadCharacterBankTabSettings(PreparedQueryResult result);
void _LoadAccountBankTabSettings(PreparedQueryResult result);
void _LoadAccountBankItems(PreparedQueryResult result, uint32 timeDiff);
/*********************************************************/
/*** SAVE SYSTEM ***/
@@ -3268,6 +3285,8 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void _SaveCUFProfiles(CharacterDatabaseTransaction trans);
void _SavePlayerData(CharacterDatabaseTransaction trans);
void _SaveCharacterBankTabSettings(CharacterDatabaseTransaction trans) const;
void _SaveAccountBankTabSettings(CharacterDatabaseTransaction trans) const;
void _SaveAccountBankItems(CharacterDatabaseTransaction trans);
/*********************************************************/
/*** ENVIRONMENTAL SYSTEM ***/
@@ -269,3 +269,34 @@ FactionTemplateEntry const* PlayerTaxi::GetFlightMasterFactionTemplate() const
{
return sFactionTemplateStore.LookupEntry(m_flightMasterFactionId);
}
//WowCommunity
TaxiMask PlayerTaxi::LoadTaxiMaskFromString(std::string const& data)
{
TaxiMask mask;
std::vector<std::string_view> tokens = Trinity::Tokenize(data, ' ', false);
for (size_t index = 0; (index < mask.size()) && (index < tokens.size()); ++index)
{
if (Optional<uint32> val = Trinity::StringTo<uint32>(tokens[index]))
mask[index] = sTaxiNodesMask[index] & *val;
else
mask[index] = 0;
}
return mask;
}
void PlayerTaxi::MergeAccountTaxiMask(TaxiMask const& accountMask)
{
for (TaxiNodesEntry const* node : sTaxiNodesStore)
{
if (node->GetFlags().HasFlag(TaxiNodeFlags::NotAccountWide))
continue;
uint32 field = uint32((node->ID - 1) / (sizeof(TaxiMask::value_type) * 8));
TaxiMask::value_type submask = TaxiMask::value_type(1 << ((node->ID - 1) % (sizeof(TaxiMask::value_type) * 8)));
if (accountMask[field] & submask)
m_taximask[field] |= submask;
}
}
@@ -47,6 +47,11 @@ class TC_GAME_API PlayerTaxi
void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level);
bool LoadTaxiMask(std::string const& data);
//WowCommunity
static TaxiMask LoadTaxiMaskFromString(std::string const& data);
void MergeAccountTaxiMask(TaxiMask const& accountMask);
//WowCommunity
bool IsTaximaskNodeKnown(uint32 nodeidx) const
{
uint32 field = uint32((nodeidx - 1) / (sizeof(TaxiMask::value_type) * 8));
+67 -6
View File
@@ -37,15 +37,24 @@ void WorldSession::HandleAutoBankItemOpcode(WorldPackets::Bank::AutoBankItem& pa
return;
}
if (packet.BankType != BankType::Character)
return;
Item* item = _player->GetItemByPos(packet.Bag, packet.Slot);
if (!item)
return;
ItemPosCountVec dest;
InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, item, false);
InventoryResult msg = EQUIP_ERR_OK;
if (packet.BankType == BankType::Account)
{
msg = _player->CanAccountBankItem(NULL_BAG, NULL_SLOT, dest, item, false);
}
else if (packet.BankType == BankType::Character)
{
msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, item, false);
}
else
return;
if (msg != EQUIP_ERR_OK)
{
_player->SendEquipError(msg, item, nullptr);
@@ -116,7 +125,7 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPackets::Bank::AutoStoreBa
if (!item)
return;
if (_player->IsBankPos(packet.Bag, packet.Slot)) // moving from bank to inventory
if (_player->IsBankPos(packet.Bag, packet.Slot)) // moving from character bank to inventory
{
ItemPosCountVec dest;
InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, item, false);
@@ -129,7 +138,20 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPackets::Bank::AutoStoreBa
_player->RemoveItem(packet.Bag, packet.Slot, true);
if (Item const* storedItem = _player->StoreItem(dest, item, true))
_player->ItemAddedQuestCheck(storedItem->GetEntry(), storedItem->GetCount());
}
else if (_player->IsAccountBankPos(packet.Bag, packet.Slot)) // moving from account bank to inventory
{
ItemPosCountVec dest;
InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, item, false);
if (msg != EQUIP_ERR_OK)
{
_player->SendEquipError(msg, item, nullptr);
return;
}
_player->RemoveItem(packet.Bag, packet.Slot, true);
if (Item const* storedItem = _player->StoreItem(dest, item, true))
_player->ItemAddedQuestCheck(storedItem->GetEntry(), storedItem->GetCount());
}
else // moving from inventory to bank
{
@@ -155,7 +177,7 @@ void WorldSession::HandleBuyBankTab(WorldPackets::Bank::BuyBankTab const& buyBan
return;
}
if (buyBankTab.BankType != BankType::Character)
if (buyBankTab.BankType != BankType::Character && buyBankTab.BankType != BankType::Account)
{
TC_LOG_DEBUG("network", "WorldSession::HandleBuyBankTab {} - Bank type {} is not supported.",
_player->GetGUID(), buyBankTab.BankType);
@@ -313,3 +335,42 @@ void WorldSession::SendShowBank(ObjectGuid guid, PlayerInteractionType interacti
npcInteraction.Success = true;
SendPacket(npcInteraction.Write());
}
void WorldSession::HandleAutoDepositAccountBank(WorldPackets::Bank::AutoDepositAccountBank const& autoDepositAccountBank)
{
if (!CanUseBank(autoDepositAccountBank.Banker))
{
TC_LOG_DEBUG("network", "WORLD: HandleAutoDepositAccountBank - {} not found or you can't interact with him.", autoDepositAccountBank.Banker);
return;
}
if (_player->GetAccountBankTabCount() == 0)
{
_player->SendEquipError(EQUIP_ERR_BANK_FULL);
return;
}
// Deposit all warbound/BoA items from inventory
bool anyDeposited = false;
for (Item* item : _player->GetWarboundItemsToDeposit())
{
ItemPosCountVec dest;
InventoryResult msg = _player->CanAccountBankItem(NULL_BAG, NULL_SLOT, dest, item, false);
if (msg != EQUIP_ERR_OK)
{
if (msg != EQUIP_ERR_BANK_FULL || !anyDeposited)
_player->SendEquipError(msg, item, nullptr);
break;
}
if (dest.size() == 1 && dest[0].pos == item->GetPos())
{
_player->SendEquipError(EQUIP_ERR_CANT_SWAP, item, nullptr);
continue;
}
_player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true);
_player->BankItem(dest, item, true);
anyDeposited = true;
}
}
+141 -6
View File
@@ -72,12 +72,14 @@ class LoginQueryHolder : public CharacterDatabaseQueryHolder
{
private:
uint32 m_accountId;
uint32 m_battlenetAccountId;
ObjectGuid m_guid;
public:
LoginQueryHolder(uint32 accountId, ObjectGuid guid)
: m_accountId(accountId), m_guid(guid) { }
LoginQueryHolder(uint32 accountId, uint32 battlenetAccountId, ObjectGuid guid)
: m_accountId(accountId), m_battlenetAccountId(battlenetAccountId), m_guid(guid) { }
ObjectGuid GetGuid() const { return m_guid; }
uint32 GetAccountId() const { return m_accountId; }
uint32 GetBattlenetAccountId() const { return m_battlenetAccountId; }
bool Initialize();
};
@@ -367,6 +369,14 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BANK_TAB_SETTINGS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_BANK_TAB_SETTINGS);
stmt->setUInt32(0, m_battlenetAccountId);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_BANK_TAB_SETTINGS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_BANK_ITEMS);
stmt->setUInt32(0, m_battlenetAccountId);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_BANK_ITEMS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_HOUSING);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_HOUSING, stmt);
@@ -387,6 +397,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_HOUSING_CATALOG, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WARBAND_TAXI_MASK);
stmt->setUInt32(0, m_battlenetAccountId);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_WARBAND_TAXI_MASK, stmt);
return res;
}
@@ -408,9 +422,10 @@ public:
SetSize(MAX);
}
bool Initialize(uint32 accountId, bool withDeclinedNames, bool isDeletedCharacters)
bool Initialize(uint32 accountId, uint32 battlenetAccountId, bool withDeclinedNames, bool isDeletedCharacters)
{
_isDeletedCharacters = isDeletedCharacters;
_battlenetAccountId = battlenetAccountId;
constexpr CharacterDatabaseStatements statements[2][3] =
{
@@ -427,13 +442,26 @@ public:
stmt->setUInt32(0, accountId);
result &= SetPreparedQuery(CUSTOMIZATIONS, stmt);
if (!isDeletedCharacters)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WARBAND_GROUPS);
stmt->setUInt32(0, battlenetAccountId);
result &= SetPreparedQuery(WARBAND_GROUPS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WARBAND_GROUP_MEMBERS);
stmt->setUInt32(0, battlenetAccountId);
result &= SetPreparedQuery(WARBAND_GROUP_MEMBERS, stmt);
}
return result;
}
bool IsDeletedCharacters() const { return _isDeletedCharacters; }
uint32 GetBattlenetAccountId() const { return _battlenetAccountId; }
private:
bool _isDeletedCharacters = false;
uint32 _battlenetAccountId = 0;
};
void WorldSession::HandleCharEnum(CharacterDatabaseQueryHolder const& holder)
@@ -514,6 +542,113 @@ void WorldSession::HandleCharEnum(CharacterDatabaseQueryHolder const& holder)
charEnum.RaceUnlockData.push_back(raceUnlock);
}
if (!charEnum.IsDeletedCharacters)
{
EnumCharactersQueryHolder const& enumHolder = static_cast<EnumCharactersQueryHolder const&>(holder);
// Load existing warband groups from DB
std::unordered_map<uint64, WorldPackets::Character::WarbandGroup*> groupsByDbId;
if (PreparedQueryResult groupResult = holder.GetPreparedResult(EnumCharactersQueryHolder::WARBAND_GROUPS))
{
do
{
Field* fields = groupResult->Fetch();
WorldPackets::Character::WarbandGroup& group = charEnum.WarbandGroups.emplace_back();
group.GroupID = fields[0].GetUInt64();
group.OrderIndex = fields[1].GetUInt8();
group.WarbandSceneID = fields[2].GetUInt32();
group.Flags = fields[3].GetUInt32();
group.ContentSetID = fields[4].GetInt32();
group.Name = fields[5].GetString();
groupsByDbId[group.GroupID] = &group;
} while (groupResult->NextRow());
}
if (PreparedQueryResult memberResult = holder.GetPreparedResult(EnumCharactersQueryHolder::WARBAND_GROUP_MEMBERS))
{
do
{
Field* fields = memberResult->Fetch();
uint64 groupId = fields[0].GetUInt64();
auto it = groupsByDbId.find(groupId);
if (it == groupsByDbId.end())
continue;
WorldPackets::Character::WarbandGroupMember member;
// fields[1] is memberIndex - used for ordering, implicit from vector position
member.Guid = ObjectGuid::Create<HighGuid::Player>(fields[2].GetUInt64());
member.WarbandScenePlacementID = fields[3].GetUInt32();
member.Type = fields[4].GetInt32();
member.ContentSetID = fields[5].GetInt32();
it->second->Members.push_back(member);
} while (memberResult->NextRow());
}
// If no warband groups exist and we have characters, create a default group
if (charEnum.WarbandGroups.empty() && !charEnum.Characters.empty())
{
// Use the first available warband scene (ID 1 = default "Campfire" scene)
uint32 defaultSceneId = 0;
for (WarbandSceneEntry const* scene : sWarbandSceneStore)
{
defaultSceneId = scene->ID;
break;
}
if (defaultSceneId != 0)
{
WorldPackets::Character::WarbandGroup& defaultGroup = charEnum.WarbandGroups.emplace_back();
defaultGroup.OrderIndex = 0;
defaultGroup.WarbandSceneID = defaultSceneId;
defaultGroup.Flags = 0;
defaultGroup.ContentSetID = 0;
// Get valid placement IDs for this scene (only character slots, type 0)
std::vector<uint32> characterPlacementIds;
if (std::vector<WarbandScenePlacementEntry const*> const* placements = sDB2Manager.GetWarbandScenePlacements(defaultSceneId))
{
for (WarbandScenePlacementEntry const* placement : *placements)
{
if (placement->SlotType == 0) // Character slot
characterPlacementIds.push_back(placement->ID);
}
}
// Assign up to 4 characters (or as many as we have placement slots)
uint32 maxMembers = std::min<uint32>(static_cast<uint32>(charEnum.Characters.size()), static_cast<uint32>(characterPlacementIds.size()));
for (uint32 i = 0; i < maxMembers; ++i)
{
WorldPackets::Character::WarbandGroupMember member;
member.Guid = charEnum.Characters[i].Basic.Guid;
member.WarbandScenePlacementID = characterPlacementIds[i];
member.Type = 0; // Character
member.ContentSetID = 0;
defaultGroup.Members.push_back(member);
}
// Persist the default group asynchronously
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_WARBAND_GROUP);
stmt->setUInt64(0, 0); // AUTO_INCREMENT - will be assigned by DB
stmt->setUInt32(1, enumHolder.GetBattlenetAccountId());
stmt->setUInt8(2, 0); // orderIndex
stmt->setUInt32(3, defaultSceneId);
stmt->setUInt32(4, 0); // flags
stmt->setInt32(5, 0); // contentSetId
stmt->setString(6, std::string());
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
// We need the auto-generated groupId for subsequent member inserts.
// Since we can't get LAST_INSERT_ID in an async transaction easily,
// we'll set a temporary GroupID of 0 in the packet (client doesn't send it back).
// The members will be properly persisted when the client sends CMSG_SETUP_WARBAND_GROUPS.
}
}
}
SendPacket(charEnum.Write());
if (!charEnum.IsDeletedCharacters)
@@ -528,7 +663,7 @@ void WorldSession::HandleCharEnumOpcode(WorldPackets::Character::EnumCharacters&
/// get all the data necessary for loading all characters (along with their pets) on the account
std::shared_ptr<EnumCharactersQueryHolder> holder = std::make_shared<EnumCharactersQueryHolder>();
if (!holder->Initialize(GetAccountId(), sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED), false))
if (!holder->Initialize(GetAccountId(), GetBattlenetAccountId(), sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED), false))
{
HandleCharEnum(*holder);
return;
@@ -544,7 +679,7 @@ void WorldSession::HandleCharUndeleteEnumOpcode(WorldPackets::Character::EnumCha
{
/// get all the data necessary for loading all undeleted characters (along with their pets) on the account
std::shared_ptr<EnumCharactersQueryHolder> holder = std::make_shared<EnumCharactersQueryHolder>();
if (!holder->Initialize(GetAccountId(), sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED), true))
if (!holder->Initialize(GetAccountId(), GetBattlenetAccountId(), sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED), true))
{
HandleCharEnum(*holder);
return;
@@ -1148,7 +1283,7 @@ void WorldSession::HandleContinuePlayerLogin()
return;
}
std::shared_ptr<LoginQueryHolder> holder = std::make_shared<LoginQueryHolder>(GetAccountId(), m_playerLoading);
std::shared_ptr<LoginQueryHolder> holder = std::make_shared<LoginQueryHolder>(GetAccountId(), GetBattlenetAccountId(), m_playerLoading);
if (!holder->Initialize())
{
m_playerLoading.Clear();
+3 -3
View File
@@ -163,7 +163,7 @@ void WorldSession::HandleSendMail(WorldPackets::Mail::SendMail& sendMail)
if (Item* item = player->GetItemByGuid(att.ItemGUID))
{
ItemTemplate const* itemProto = item->GetTemplate();
if (!itemProto || !itemProto->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT))
if (!itemProto || !(itemProto->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT) || item->IsWarbandBound()))
{
accountBound = false;
break;
@@ -215,9 +215,9 @@ void WorldSession::HandleSendMail(WorldPackets::Mail::SendMail& sendMail)
return;
}
if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId)
if ((item->IsBoundAccountWide() || item->IsWarbandBound()) && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId)
{
if (!item->IsBattlenetAccountBound() || !player->GetSession()->GetBattlenetAccountId() || player->GetSession()->GetBattlenetAccountId() != receiverBnetAccountId)
if (!(item->IsBattlenetAccountBound() || item->IsWarbandBound()) || !player->GetSession()->GetBattlenetAccountId() || player->GetSession()->GetBattlenetAccountId() != receiverBnetAccountId)
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_NOT_SAME_ACCOUNT);
return;
@@ -766,6 +766,15 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPackets::Trade::SetTradeItem& s
my_trade->UpdateClientStateIndex();
// warbound items cannot be traded to other players
if (setTradeItem.TradeSlot != TRADE_SLOT_NONTRADED && item->IsWarbandBound())
{
info.Status = TRADE_STATUS_NOT_ON_TAPLIST;
info.TradeSlot = setTradeItem.TradeSlot;
SendTradeStatus(info);
return;
}
if (setTradeItem.TradeSlot != TRADE_SLOT_NONTRADED && item->IsBindedNotWith(my_trade->GetTrader()))
{
info.Status = TRADE_STATUS_NOT_ON_TAPLIST;
@@ -74,4 +74,10 @@ void UpdateBankTabSettings::Read()
_worldPacket >> Tab;
_worldPacket >> Settings;
}
void AutoDepositAccountBank::Read()
{
_worldPacket >> Banker;
}
}
@@ -106,6 +106,17 @@ namespace WorldPackets
uint8 Tab = 0;
BankTabSettings Settings;
};
class AutoDepositAccountBank final : public ClientPacket
{
public:
explicit AutoDepositAccountBank(WorldPacket&& packet) : ClientPacket(CMSG_AUTO_DEPOSIT_ACCOUNT_BANK, std::move(packet)) {}
void Read() override;
ObjectGuid Banker;
};
}
}
+1 -1
View File
@@ -195,7 +195,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_AUTH_SESSION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess);
DEFINE_HANDLER(CMSG_AUTOBANK_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleAutoBankItemOpcode);
DEFINE_HANDLER(CMSG_AUTOSTORE_BANK_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleAutoStoreBankItemOpcode);
DEFINE_HANDLER(CMSG_AUTO_DEPOSIT_ACCOUNT_BANK, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_AUTO_DEPOSIT_ACCOUNT_BANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoDepositAccountBank);
DEFINE_HANDLER(CMSG_AUTO_DEPOSIT_CHARACTER_BANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoDepositCharacterBank);
DEFINE_HANDLER(CMSG_AUTO_EQUIP_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleAutoEquipItemOpcode);
DEFINE_HANDLER(CMSG_AUTO_EQUIP_ITEM_SLOT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleAutoEquipItemSlotOpcode);
+2
View File
@@ -183,6 +183,7 @@ namespace WorldPackets
class BuyBankTab;
class UpdateBankTabSettings;
class AutoDepositCharacterBank;
class AutoDepositAccountBank;
class BankerActivate;
}
@@ -1922,6 +1923,7 @@ class TC_GAME_API WorldSession
void HandleBuyBankTab(WorldPackets::Bank::BuyBankTab const& buyBankTab);
void HandleUpdateBankTabSettings(WorldPackets::Bank::UpdateBankTabSettings const& updateBankTabSettings);
void HandleAutoDepositCharacterBank(WorldPackets::Bank::AutoDepositCharacterBank const& autoDepositCharacterBank);
void HandleAutoDepositAccountBank(WorldPackets::Bank::AutoDepositAccountBank const& autoDepositAccountBank);
// Black Market
void HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketOpen& blackMarketOpen);