diff options
| author | Shauren <none@none> | 2010-06-25 13:15:51 +0200 |
|---|---|---|
| committer | Shauren <none@none> | 2010-06-25 13:15:51 +0200 |
| commit | 456c6291ab70f2f3182a4d63408c45099eedbbdd (patch) | |
| tree | 21aa76d9107d54a70572d5ac450362fce276ce74 | |
| parent | eb79fb91a6eb410a5a5ceb6259b9d4bc5569c2ae (diff) | |
Move trade data to dynamic structure and added support for applying enchant after trade ends, based on Vladimir's commits
--HG--
branch : trunk
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 150 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.h | 65 | ||||
| -rw-r--r-- | src/server/game/Miscellaneous/SharedDefines.h | 27 | ||||
| -rw-r--r-- | src/server/game/Server/Protocol/Handlers/TradeHandler.cpp | 499 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.h | 4 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 77 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.h | 1 |
7 files changed, 552 insertions, 271 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 543aeffd52e..83fa4fa34fe 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -280,6 +280,105 @@ std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi) return ss; } +//== TradeData ================================================= + +TradeData* TradeData::GetTraderData() const +{ + return m_trader->GetTradeData(); +} + +Item* TradeData::GetItem(TradeSlots slot) const +{ + return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : NULL; +} + +bool TradeData::HasItem(uint64 item_guid) const +{ + for(uint8 i = 0; i < TRADE_SLOT_COUNT; ++i) + if (m_items[i] == item_guid) + return true; + + return false; +} + +Item* TradeData::GetSpellCastItem() const +{ + return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : NULL; +} + +void TradeData::SetItem(TradeSlots slot, Item* item) +{ + uint64 itemGuid = item ? item->GetGUID() : 0; + + if (m_items[slot] == itemGuid) + return; + + m_items[slot] = itemGuid; + + SetAccepted(false); + GetTraderData()->SetAccepted(false); + + Update(); + + // need remove possible trader spell applied to changed item + if (slot == TRADE_SLOT_NONTRADED) + GetTraderData()->SetSpell(0); + + // need remove possible player spell applied (possible move reagent) + SetSpell(0); +} + +void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= NULL*/) +{ + uint64 itemGuid = castItem ? castItem->GetGUID() : 0; + + if (m_spell == spell_id && m_spellCastItem == itemGuid) + return; + + m_spell = spell_id; + m_spellCastItem = itemGuid; + + SetAccepted(false); + GetTraderData()->SetAccepted(false); + + Update(true); // send spell info to item owner + Update(false); // send spell info to caster self +} + +void TradeData::SetMoney(uint32 money) +{ + if (m_money == money) + return; + + m_money = money; + + SetAccepted(false); + GetTraderData()->SetAccepted(false); + + Update(true); +} + +void TradeData::Update(bool forTarget /*= true*/) +{ + if (forTarget) + m_trader->GetSession()->SendUpdateTrade(true); // player state for trader + else + m_player->GetSession()->SendUpdateTrade(false); // player state for player +} + +void TradeData::SetAccepted(bool state, bool crosssend /*= false*/) +{ + m_accepted = state; + + if (!state) + { + if (crosssend) + m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + else + m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + } +} + // == Player ==================================================== UpdateMask Player::updateVisualBits; @@ -354,8 +453,7 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa m_bHasDelayedTeleport = false; m_teleport_options = 0; - pTrader = 0; - ClearTrade(); + m_trade = NULL; m_cinematic = 0; @@ -9539,7 +9637,7 @@ bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++) { Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item) + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) { tempcount += pItem->GetCount(); if (tempcount >= count) @@ -9549,7 +9647,7 @@ bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) { Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item) + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) { tempcount += pItem->GetCount(); if (tempcount >= count) @@ -9563,7 +9661,7 @@ bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const for (uint32 j = 0; j < pBag->GetBagSize(); j++) { Item* pItem = GetItemByPos(i, j); - if (pItem && pItem->GetEntry() == item) + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) { tempcount += pItem->GetCount(); if (tempcount >= count) @@ -9578,7 +9676,7 @@ bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++) { Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item) + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) { tempcount += pItem->GetCount(); if (tempcount >= count) @@ -9592,7 +9690,7 @@ bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const for (uint32 j = 0; j < pBag->GetBagSize(); j++) { Item* pItem = GetItemByPos(i, j); - if (pItem && pItem->GetEntry() == item) + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) { tempcount += pItem->GetCount(); if (tempcount >= count) @@ -11813,7 +11911,7 @@ void Player::DestroyItemCount(uint32 item, uint32 count, bool update, bool unequ { if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - if (pItem->GetEntry() == item) + if (pItem->GetEntry() == item && !pItem->IsInTrade()) { if (pItem->GetCount() + remcount <= count) { @@ -11841,7 +11939,7 @@ void Player::DestroyItemCount(uint32 item, uint32 count, bool update, bool unequ { if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - if (pItem->GetEntry() == item) + if (pItem->GetEntry() == item && !pItem->IsInTrade()) { if (pItem->GetCount() + remcount <= count) { @@ -11874,7 +11972,7 @@ void Player::DestroyItemCount(uint32 item, uint32 count, bool update, bool unequ { if (Item* pItem = pBag->GetItemByPos(j)) { - if (pItem->GetEntry() == item) + if (pItem->GetEntry() == item && !pItem->IsInTrade()) { // all items in bags can be unequipped if (pItem->GetCount() + remcount <= count) @@ -11905,7 +12003,7 @@ void Player::DestroyItemCount(uint32 item, uint32 count, bool update, bool unequ { if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - if (pItem && pItem->GetEntry() == item) + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) { if (pItem->GetCount() + remcount <= count) { @@ -12663,33 +12761,23 @@ void Player::SendSellError(uint8 msg, Creature* pCreature, uint64 guid, uint32 p GetSession()->SendPacket(&data); } -void Player::ClearTrade() -{ - tradeGold = 0; - acceptTrade = false; - for (uint8 i = 0; i < TRADE_SLOT_COUNT; i++) - tradeItems[i] = 0; -} - void Player::TradeCancel(bool sendback) { - if (pTrader) + if (m_trade) { + Player* trader = m_trade->GetTrader(); + // send yellow "Trade canceled" message to both traders - WorldSession* ws; - ws = GetSession(); if (sendback) - ws->SendCancelTrade(); - ws = pTrader->GetSession(); - if (!ws->PlayerLogout()) - ws->SendCancelTrade(); + GetSession()->SendCancelTrade(); + + trader->GetSession()->SendCancelTrade(); // cleanup - ClearTrade(); - pTrader->ClearTrade(); - // prevent loss of reference - pTrader->pTrader = NULL; - pTrader = NULL; + delete m_trade; + m_trade = NULL; + delete trader->m_trade; + trader->m_trade = NULL; } } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 0025d9f55d0..99584b5bf64 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -920,6 +920,55 @@ struct BGData bool HasTaxiPath() const { return taxiPath[0] && taxiPath[1]; } }; +class TradeData +{ + public: // constructors + TradeData(Player* player, Player* trader) : + m_player(player), m_trader(trader), m_accepted(false), m_acceptProccess(false), + m_money(0), m_spell(0) {} + + Player* GetTrader() const { return m_trader; } + TradeData* GetTraderData() const; + + Item* GetItem(TradeSlots slot) const; + bool HasItem(uint64 item_guid) const; + void SetItem(TradeSlots slot, Item* item); + + uint32 GetSpell() const { return m_spell; } + void SetSpell(uint32 spell_id, Item* castItem = NULL); + + Item* GetSpellCastItem() const; + bool HasSpellCastItem() const { return m_spellCastItem != 0; } + + uint32 GetMoney() const { return m_money; } + void SetMoney(uint32 money); + + bool IsAccepted() const { return m_accepted; } + void SetAccepted(bool state, bool crosssend = false); + + bool IsInAcceptProcess() const { return m_acceptProccess; } + void SetInAcceptProcess(bool state) { m_acceptProccess = state; } + + private: // internal functions + + void Update(bool for_trader = true); + + private: // fields + + Player* m_player; // Player who own of this TradeData + Player* m_trader; // Player who trade with m_player + + bool m_accepted; // m_player press accept for trade list + bool m_acceptProccess; // one from player/trader press accept and this processed + + uint32 m_money; // m_player place money to trade + + uint32 m_spell; // m_player apply spell to non-traded slot item + uint64 m_spellCastItem; // applied spell casted by item use + + uint64 m_items[TRADE_SLOT_COUNT]; // traded itmes from m_player side including non-traded slot +}; + class Player : public Unit, public GridObject<Player> { friend class WorldSession; @@ -1191,15 +1240,10 @@ class Player : public Unit, public GridObject<Player> bool BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot); float GetReputationPriceDiscount(Creature const* pCreature) const; - Player* GetTrader() const { return pTrader; } - void ClearTrade(); + + Player* GetTrader() const { return m_trade ? m_trade->GetTrader() : NULL; } + TradeData* GetTradeData() const { return m_trade; } void TradeCancel(bool sendback); - Item *GetItemByTradeSlot(uint8 slot) const - { - if (slot < TRADE_SLOT_COUNT && tradeItems[slot]) - return GetItemByGuid(tradeItems[slot]); - return NULL; - } void UpdateEnchantTime(uint32 time); void UpdateItemDuration(uint32 time, bool realtimeonly=false); @@ -2460,10 +2504,7 @@ class Player : public Unit, public GridObject<Player> int m_cinematic; - Player *pTrader; - bool acceptTrade; - uint64 tradeItems[TRADE_SLOT_COUNT]; - uint32 tradeGold; + TradeData* m_trade; bool m_DailyQuestChanged; bool m_WeeklyQuestChanged; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 381114c6f38..a9ae3bc4cc0 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -2769,4 +2769,31 @@ enum SpellFamilyNames SPELLFAMILY_PET = 17 }; +enum TradeStatus +{ + TRADE_STATUS_BUSY = 0, + TRADE_STATUS_BEGIN_TRADE = 1, + TRADE_STATUS_OPEN_WINDOW = 2, + TRADE_STATUS_TRADE_CANCELED = 3, + TRADE_STATUS_TRADE_ACCEPT = 4, + TRADE_STATUS_BUSY_2 = 5, + TRADE_STATUS_NO_TARGET = 6, + TRADE_STATUS_BACK_TO_TRADE = 7, + TRADE_STATUS_TRADE_COMPLETE = 8, + // 9? + TRADE_STATUS_TARGET_TO_FAR = 10, + TRADE_STATUS_WRONG_FACTION = 11, + TRADE_STATUS_CLOSE_WINDOW = 12, + // 13? + TRADE_STATUS_IGNORE_YOU = 14, + TRADE_STATUS_YOU_STUNNED = 15, + TRADE_STATUS_TARGET_STUNNED = 16, + TRADE_STATUS_YOU_DEAD = 17, + TRADE_STATUS_TARGET_DEAD = 18, + TRADE_STATUS_YOU_LOGOUT = 19, + TRADE_STATUS_TARGET_LOGOUT = 20, + TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action + TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related). +}; + #endif diff --git a/src/server/game/Server/Protocol/Handlers/TradeHandler.cpp b/src/server/game/Server/Protocol/Handlers/TradeHandler.cpp index 448a6e0520d..6191b88587e 100644 --- a/src/server/game/Server/Protocol/Handlers/TradeHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/TradeHandler.cpp @@ -27,37 +27,11 @@ #include "Opcodes.h" #include "Player.h" #include "Item.h" +#include "Spell.h" #include "SocialMgr.h" #include "Language.h" -enum TradeStatus -{ - TRADE_STATUS_BUSY = 0, - TRADE_STATUS_BEGIN_TRADE = 1, - TRADE_STATUS_OPEN_WINDOW = 2, - TRADE_STATUS_TRADE_CANCELED = 3, - TRADE_STATUS_TRADE_ACCEPT = 4, - TRADE_STATUS_BUSY_2 = 5, - TRADE_STATUS_NO_TARGET = 6, - TRADE_STATUS_BACK_TO_TRADE = 7, - TRADE_STATUS_TRADE_COMPLETE = 8, - // 9? - TRADE_STATUS_TARGET_TO_FAR = 10, - TRADE_STATUS_WRONG_FACTION = 11, - TRADE_STATUS_CLOSE_WINDOW = 12, - // 13? - TRADE_STATUS_IGNORE_YOU = 14, - TRADE_STATUS_YOU_STUNNED = 15, - TRADE_STATUS_TARGET_STUNNED = 16, - TRADE_STATUS_YOU_DEAD = 17, - TRADE_STATUS_TARGET_DEAD = 18, - TRADE_STATUS_YOU_LOGOUT = 19, - TRADE_STATUS_TARGET_LOGOUT = 20, - TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action - TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related). -}; - -void WorldSession::SendTradeStatus(uint32 status) +void WorldSession::SendTradeStatus(TradeStatus status) { WorldPacket data; @@ -106,68 +80,50 @@ void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/) // recvPacket.print_storage(); } -void WorldSession::SendUpdateTrade() +void WorldSession::SendUpdateTrade(bool trader_data /*= true*/) { - Item *item = NULL; + TradeData* view_trade = trader_data ? _player->GetTradeData()->GetTraderData() : _player->GetTradeData(); - if (!_player || !_player->pTrader) - return; + WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, 1+4+4+4+4+4+7*(1+4+4+4+4+8+4+4+4+4+8+4+4+4+4+4+4)); + data << uint8(trader_data); // 1 means traders data, 0 means own + data << uint32(0); // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?) + data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = next field in most cases + data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = prev field in most cases + data << uint32(view_trade->GetMoney()); // trader gold + data << uint32(view_trade->GetSpell()); // spell casted on lowest slot item - // reset trade status - if (_player->acceptTrade) - { - _player->acceptTrade = false; - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } - - if (_player->pTrader->acceptTrade) - { - _player->pTrader->acceptTrade = false; - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } - - WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size - data << (uint8) 1; // can be different (only seen 0 and 1) - data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?) - data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = next field in most cases - data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases - data << (uint32) _player->pTrader->tradeGold; // trader gold - data << (uint32) 0; // spell casted on lowest slot item + Item *item = NULL; for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i) { - item = (_player->pTrader->tradeItems[i] != 0 ? _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]) : NULL); + data << uint8(i); // trade slot number, if not specified, then end of packet - data << (uint8) i; // trade slot number, if not specified, then end of packet - - if (item) + if (Item* item = view_trade->GetItem(TradeSlots(i))) { - data << (uint32) item->GetProto()->ItemId; // entry - // display id - data << (uint32) item->GetProto()->DisplayInfoID; - // stack count - data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT); - data << (uint32) 0; // probably gift=1, created_by=0? - // gift creator - data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR); - data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT); - for (uint8 j = 0; j < 3; ++j) - data << (uint32) 0; // enchantment id (permanent/gems?) + data << uint32(item->GetProto()->ItemId); // entry + data << uint32(item->GetProto()->DisplayInfoID);// display id + data << uint32(item->GetCount()); // stack count + // wrapped: hide stats but show giftcreator name + data << uint32(item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED) ? 1 : 0); + data << uint64(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)); + // perm. enchantment and gems + data << uint32(item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)); + for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot) + data << uint32(item->GetEnchantmentId(EnchantmentSlot(enchant_slot))); // creator - data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR); - data << (uint32) item->GetSpellCharges(); // charges - data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor - // random properties id - data << (int32) item->GetItemRandomPropertyId(); - data << (uint32) item->GetProto()->LockID; // lock id + data << uint64(item->GetUInt64Value(ITEM_FIELD_CREATOR)); + data << uint32(item->GetSpellCharges()); // charges + data << uint32(item->GetItemSuffixFactor()); // SuffixFactor + data << uint32(item->GetItemRandomPropertyId());// random properties id + data << uint32(item->GetProto()->LockID); // lock id // max durability - data << (uint32) item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); + data << uint32(item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)); // durability - data << (uint32) item->GetUInt32Value(ITEM_FIELD_DURABILITY); + data << uint32(item->GetUInt32Value(ITEM_FIELD_DURABILITY)); } else { - for (uint8 j = 0; j < 18; j++) + for (uint8 j = 0; j < 18; ++j) data << uint32(0); } } @@ -179,11 +135,15 @@ void WorldSession::SendUpdateTrade() void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) { - for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) + Player* trader = _player->GetTrader(); + if (!trader) + return; + + for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { ItemPosCountVec traderDst; ItemPosCountVec playerDst; - bool traderCanTrade = (myItems[i] == NULL || _player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK); + bool traderCanTrade = (myItems[i] == NULL || trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK); bool playerCanTrade = (hisItems[i] == NULL || _player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK); if (traderCanTrade && playerCanTrade) { @@ -196,25 +156,25 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) sLog.outDebug("partner storing: %u",myItems[i]->GetGUIDLow()); if (_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { - sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", - _player->GetName(),_player->GetSession()->GetAccountId(), - myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(), - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); + sLog.outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", + _player->GetName(), _player->GetSession()->GetAccountId(), + myItems[i]->GetProto()->Name1, myItems[i]->GetEntry(), myItems[i]->GetCount(), + trader->GetName(), trader->GetSession()->GetAccountId()); } // store - _player->pTrader->MoveItemToInventory(traderDst, myItems[i], true, true); + trader->MoveItemToInventory(traderDst, myItems[i], true, true); } if (hisItems[i]) { // logging sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow()); - if (_player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + if (trader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { - sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(),"GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(), - hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(), - _player->GetName(),_player->GetSession()->GetAccountId()); + sLog.outCommand(trader->GetSession()->GetAccountId(),"GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", + trader->GetName(), trader->GetSession()->GetAccountId(), + hisItems[i]->GetProto()->Name1, hisItems[i]->GetEntry(), hisItems[i]->GetCount(), + _player->GetName(), _player->GetSession()->GetAccountId()); } // store @@ -239,8 +199,8 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) { if (!playerCanTrade) sLog.outError("player can't store item: %u",hisItems[i]->GetGUIDLow()); - if (_player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK) - _player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true); + if (trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK) + trader->MoveItemToInventory(traderDst, hisItems[i], true, true); else sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow()); } @@ -250,135 +210,231 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) //============================================================== +static void setAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade, Item **myItems, Item **hisItems) +{ + myTrade->SetInAcceptProcess(true); + hisTrade->SetInAcceptProcess(true); + + // store items in local list and set 'in-trade' flag + for(uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) + { + if (Item* item = myTrade->GetItem(TradeSlots(i))) + { + DEBUG_LOG("player trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot()); + //Can return NULL + myItems[i] = item; + myItems[i]->SetInTrade(); + } + + if (Item* item = hisTrade->GetItem(TradeSlots(i))) + { + DEBUG_LOG("partner trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot()); + hisItems[i] = item; + hisItems[i]->SetInTrade(); + } + } +} + +static void clearAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade) +{ + myTrade->SetInAcceptProcess(false); + hisTrade->SetInAcceptProcess(false); +} + +static void clearAcceptTradeMode(Item **myItems, Item **hisItems) +{ + // clear 'in-trade' flag + for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) + { + if (myItems[i]) + myItems[i]->SetInTrade(false); + if (hisItems[i]) + hisItems[i]->SetInTrade(false); + } +} + void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) { + TradeData* my_trade = _player->m_trade; + if (!my_trade) + return; + + Player* trader = my_trade->GetTrader(); + + TradeData* his_trade = trader->m_trade; + if (!his_trade) + return; + Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; - bool myCanCompleteTrade=true,hisCanCompleteTrade=true; + bool myCanCompleteTrade = true, hisCanCompleteTrade = true; - if (!GetPlayer()->pTrader) - return; + // set before checks for propertly undo at problems (it already set in to client) + my_trade->SetAccepted(true); // not accept case incorrect money amount - if (_player->tradeGold > _player->GetMoney()) + if (my_trade->GetMoney() > _player->GetMoney()) { SendNotification(LANG_NOT_ENOUGH_GOLD); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->acceptTrade = false; + my_trade->SetAccepted(false, true); return; } // not accept case incorrect money amount - if (_player->pTrader->tradeGold > _player->pTrader->GetMoney()) + if (his_trade->GetMoney() > trader->GetMoney()) { - _player->pTrader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->pTrader->acceptTrade = false; + trader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD); + his_trade->SetAccepted(false, true); return; } // not accept if some items now can't be trade (cheating) - for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) + for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { - if (_player->tradeItems[i] != 0) + if (Item* item = my_trade->GetItem(TradeSlots(i))) { - if (Item* item =_player->GetItemByGuid(_player->tradeItems[i])) + if (!item->CanBeTraded()) { - if (!item->CanBeTraded()) - { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + return; } } - if (_player->pTrader->tradeItems[i] != 0) + + if (Item* item = his_trade->GetItem(TradeSlots(i))) { - if (Item* item =_player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i])) + if (!item->CanBeTraded()) { - if (!item->CanBeTraded()) - { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + return; } } } - _player->acceptTrade = true; - if (_player->pTrader->acceptTrade) + if (his_trade->IsAccepted()) { - // inform partner client - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + setAcceptTradeMode(my_trade, his_trade, myItems, hisItems); + + Spell* my_spell = NULL; + SpellCastTargets my_targets; + + Spell* his_spell = NULL; + SpellCastTargets his_targets; - // store items in local list and set 'in-trade' flag - for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) + // not accept if spell can't be casted now (cheating) + if (uint32 my_spell_id = my_trade->GetSpell()) { - if (_player->tradeItems[i] != 0) + SpellEntry const* spellEntry = sSpellStore.LookupEntry(my_spell_id); + Item* castItem = my_trade->GetSpellCastItem(); + + if (!spellEntry || !his_trade->GetItem(TRADE_SLOT_NONTRADED) || + my_trade->HasSpellCastItem() && !castItem) { - //Can return NULL - myItems[i] = _player->GetItemByGuid(_player->tradeItems[i]); - if (myItems[i]) - { - myItems[i]->SetInTrade(); - sLog.outDebug("Player trade item bag: %u slot: %u", myItems[i]->GetBagSlot(), myItems[i]->GetSlot()); - } + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + + my_trade->SetSpell(0); + return; } - if (_player->pTrader->tradeItems[i] != 0) + + my_spell = new Spell(_player, spellEntry, true); + my_spell->m_CastItem = castItem; + my_targets.setTradeItemTarget(_player); + my_spell->m_targets = my_targets; + + SpellCastResult res = my_spell->CheckCast(true); + if (res != SPELL_CAST_OK) { - //Can return NULL - hisItems[i]=_player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]); - if (hisItems[i]) - { - hisItems[i]->SetInTrade(); - sLog.outDebug("Player trade item bag: %u slot: %u", hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot()); - } + my_spell->SendCastResult(res); + + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + + delete my_spell; + my_trade->SetSpell(0); + return; } } - // test if item will fit in each inventory - hisCanCompleteTrade = (_player->pTrader->CanStoreItems(myItems,TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); - myCanCompleteTrade = (_player->CanStoreItems(hisItems,TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); - - // clear 'in-trade' flag - for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) + // not accept if spell can't be casted now (cheating) + if (uint32 his_spell_id = his_trade->GetSpell()) { - if (myItems[i]) myItems[i]->SetInTrade(false); - if (hisItems[i]) hisItems[i]->SetInTrade(false); + SpellEntry const* spellEntry = sSpellStore.LookupEntry(his_spell_id); + Item* castItem = his_trade->GetSpellCastItem(); + + if (!spellEntry || !my_trade->GetItem(TRADE_SLOT_NONTRADED) || his_trade->HasSpellCastItem() && !castItem) + { + delete my_spell; + his_trade->SetSpell(0); + + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + return; + } + + his_spell = new Spell(trader, spellEntry, true); + his_spell->m_CastItem = castItem; + his_targets.setTradeItemTarget(trader); + his_spell->m_targets = his_targets; + + SpellCastResult res = his_spell->CheckCast(true); + if (res != SPELL_CAST_OK) + { + his_spell->SendCastResult(res); + + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + + delete my_spell; + delete his_spell; + + his_trade->SetSpell(0); + return; + } } + // inform partner client + trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + + // test if item will fit in each inventory + hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); + myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); + + clearAcceptTradeMode(myItems, hisItems); + // in case of missing space report error if (!myCanCompleteTrade) { + clearAcceptTradeMode(my_trade, his_trade); + SendNotification(LANG_NOT_FREE_TRADE_SLOTS); - GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + trader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); + my_trade->SetAccepted(false); + his_trade->SetAccepted(false); return; } else if (!hisCanCompleteTrade) { + clearAcceptTradeMode(my_trade, his_trade); + SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); - GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS); + my_trade->SetAccepted(false); + his_trade->SetAccepted(false); return; } // execute trade: 1. remove - for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) + for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { - Item* iPtr = NULL; if (myItems[i]) { myItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID()); - iPtr = _player->GetItemByGuid(_player->tradeItems[i]); - _player->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true); + _player->MoveItemFromInventory(myItems[i]->GetBagSlot(), myItems[i]->GetSlot(), true); } if (hisItems[i]) { - hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID()); - iPtr = _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]); - _player->pTrader->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true); + hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, trader->GetGUID()); + trader->MoveItemFromInventory(hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot(), true); } } @@ -388,72 +444,80 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) // logging money if (sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { - if (_player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0) + if (_player->GetSession()->GetSecurity() > SEC_PLAYER && my_trade->GetMoney() > 0) { sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", - _player->GetName(),_player->GetSession()->GetAccountId(), - _player->tradeGold, - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); + _player->GetName(), _player->GetSession()->GetAccountId(), + my_trade->GetMoney(), + trader->GetName(), trader->GetSession()->GetAccountId()); } - if (_player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0) + if (trader->GetSession()->GetSecurity() > SEC_PLAYER && his_trade->GetMoney() > 0) { - sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", - _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(), - _player->pTrader->tradeGold, - _player->GetName(),_player->GetSession()->GetAccountId()); + sLog.outCommand(trader->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", + trader->GetName(), trader->GetSession()->GetAccountId(), + his_trade->GetMoney(), + _player->GetName(), _player->GetSession()->GetAccountId()); } } // update money - _player->ModifyMoney(-int32(_player->tradeGold)); - _player->ModifyMoney(_player->pTrader->tradeGold); - _player->pTrader->ModifyMoney(-int32(_player->pTrader->tradeGold)); - _player->pTrader->ModifyMoney(_player->tradeGold); + _player->ModifyMoney(-int32(my_trade->GetMoney())); + _player->ModifyMoney(his_trade->GetMoney()); + trader->ModifyMoney(-int32(his_trade->GetMoney())); + trader->ModifyMoney(my_trade->GetMoney()); + + if (my_spell) + my_spell->prepare(&my_targets); + + if (his_spell) + his_spell->prepare(&his_targets); - _player->ClearTrade(); - _player->pTrader->ClearTrade(); + // cleanup + clearAcceptTradeMode(my_trade, his_trade); + delete _player->m_trade; + _player->m_trade = NULL; + delete trader->m_trade; + trader->m_trade = NULL; // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) CharacterDatabase.BeginTransaction(); _player->SaveInventoryAndGoldToDB(); - _player->pTrader->SaveInventoryAndGoldToDB(); + trader->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); + trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); - - _player->pTrader->pTrader = NULL; - _player->pTrader = NULL; } else { - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); } } void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/) { - if (!GetPlayer()->pTrader) + TradeData* my_trade = _player->GetTradeData(); + if (!my_trade) return; - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - _player->acceptTrade = false; + my_trade->SetAccepted(false, true); } void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) { - if (!_player->pTrader) + TradeData* my_trade = _player->m_trade; + if (!my_trade) return; - _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); - _player->pTrader->ClearTrade(); - + my_trade->GetTrader()->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); - _player->ClearTrade(); } void WorldSession::SendCancelTrade() { + if (m_playerRecentlyLogout) + return; + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); } @@ -466,7 +530,7 @@ void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/) void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) { - if (GetPlayer()->pTrader) + if (GetPlayer()->m_trade) return; uint64 ID; @@ -511,7 +575,7 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) return; } - if (pOther == GetPlayer() || pOther->pTrader) + if (pOther == GetPlayer() || pOther->m_trade) { SendTradeStatus(TRADE_STATUS_BUSY); return; @@ -566,35 +630,30 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) } // OK start trade - _player->pTrader = pOther; - pOther->pTrader =_player; + _player->m_trade = new TradeData(_player, pOther); + pOther->m_trade = new TradeData(pOther, _player); WorldPacket data(SMSG_TRADE_STATUS, 12); - data << (uint32) TRADE_STATUS_BEGIN_TRADE; - data << (uint64)_player->GetGUID(); - _player->pTrader->GetSession()->SendPacket(&data); + data << uint32(TRADE_STATUS_BEGIN_TRADE); + data << uint64(_player->GetGUID()); + pOther->GetSession()->SendPacket(&data); } void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket) { - if (!_player->pTrader) - return; - uint32 gold; - recvPacket >> gold; - // gold can be incorrect, but this is checked at trade finished. - _player->tradeGold = gold; + TradeData* my_trade = _player->GetTradeData(); + if (!my_trade) + return; - _player->pTrader->GetSession()->SendUpdateTrade(); + // gold can be incorrect, but this is checked at trade finished. + my_trade->SetMoney(gold); } void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) { - if (!_player->pTrader) - return; - // send update uint8 tradeSlot; uint8 bag; @@ -604,6 +663,10 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) recvPacket >> bag; recvPacket >> slot; + TradeData* my_trade = _player->GetTradeData(); + if (!my_trade) + return; + // invalid slot number if (tradeSlot >= TRADE_SLOT_COUNT) { @@ -612,7 +675,7 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) } // check cheating, can't fail with correct client operations - Item* item = _player->GetItemByPos(bag,slot); + Item* item = _player->GetItemByPos(bag, slot); if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded())) { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); @@ -622,35 +685,29 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) uint64 iGUID = item->GetGUID(); // prevent place single item into many trade slots using cheating and client bugs - for (int i = 0; i < TRADE_SLOT_COUNT; ++i) + if (my_trade->HasItem(iGUID)) { - if (_player->tradeItems[i] == iGUID) - { - // cheating attempt - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); - return; - } + // cheating attempt + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + return; } - _player->tradeItems[tradeSlot] = iGUID; - - _player->pTrader->GetSession()->SendUpdateTrade(); + my_trade->SetItem(TradeSlots(tradeSlot), item); } void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket) { - if (!_player->pTrader) - return; - uint8 tradeSlot; recvPacket >> tradeSlot; + TradeData* my_trade = _player->m_trade; + if (!my_trade) + return; + // invalid slot number if (tradeSlot >= TRADE_SLOT_COUNT) return; - _player->tradeItems[tradeSlot] = 0; - - _player->pTrader->GetSession()->SendUpdateTrade(); + my_trade->SetItem(TradeSlots(tradeSlot), NULL); } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index c17f3e3f3e6..dbcc94f071b 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -205,12 +205,12 @@ class WorldSession void SendBattlegGroundList(uint64 guid, BattleGroundTypeId bgTypeId); - void SendTradeStatus(uint32 status); + void SendTradeStatus(TradeStatus status); void SendCancelTrade(); void SendStablePet(uint64 guid); void SendPetitionQueryOpcode(uint64 petitionguid); - void SendUpdateTrade(); + void SendUpdateTrade(bool trader_data = true); //pet void SendPetNameQuery(uint64 guid, uint32 petnumber); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a62e602fe49..bf00f54b110 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -148,6 +148,15 @@ void SpellCastTargets::setItemTarget(Item* item) m_targetMask |= TARGET_FLAG_ITEM; } +void SpellCastTargets::setTradeItemTarget(Player* caster) +{ + m_itemTargetGUID = uint64(TRADE_SLOT_NONTRADED); + m_itemTargetEntry = 0; + m_targetMask |= TARGET_FLAG_TRADE_ITEM; + + Update(caster); +} + void SpellCastTargets::setCorpseTarget(Corpse* corpse) { m_CorpseTargetGUID = corpse->GetGUID(); @@ -163,12 +172,13 @@ void SpellCastTargets::Update(Unit* caster) m_itemTarget = NULL; if (caster->GetTypeId() == TYPEID_PLAYER) { + Player *player = caster->ToPlayer(); if (m_targetMask & TARGET_FLAG_ITEM) - m_itemTarget = caster->ToPlayer()->GetItemByGuid(m_itemTargetGUID); + m_itemTarget = player->GetItemByGuid(m_itemTargetGUID); else if (m_targetMask & TARGET_FLAG_TRADE_ITEM) - if (m_itemTargetGUID == TRADE_SLOT_NONTRADED) // here it is not guid but slot. Also prevent hacking slots - if (Player* pTrader = caster->ToPlayer()->GetTrader()) - m_itemTarget = pTrader->GetItemByTradeSlot(m_itemTargetGUID); + if (m_itemTargetGUID == TRADE_SLOT_NONTRADED) // here it is not guid but slot. Also prevents hacking slots + if (TradeData* pTrade = player->GetTradeData()) + m_itemTarget = pTrade->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED); if (m_itemTarget) m_itemTargetEntry = m_itemTarget->GetEntry(); @@ -2900,6 +2910,32 @@ void Spell::cast(bool skipCheck) SetExecutedCurrently(false); return; } + + // additional check after cast bar completes (must not be in CheckCast) + // if trade not complete then remember it in trade data + if (m_targets.getTargetMask() & TARGET_FLAG_TRADE_ITEM) + { + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + if (TradeData* my_trade = m_caster->ToPlayer()->GetTradeData()) + { + if (!my_trade->IsInAcceptProcess()) + { + // Spell will be casted at completing the trade. Silently ignore at this place + my_trade->SetSpell(m_spellInfo->Id, m_CastItem); + SendCastResult(SPELL_FAILED_DONT_REPORT); + SendInterrupted(0); + m_caster->ToPlayer()->RestoreSpellMods(this); + // cleanup after mod system + // triggered spell pointer can be not removed in some cases + m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); + finish(false); + SetExecutedCurrently(false); + return; + } + } + } + } } SelectSpellTargets(); @@ -2965,6 +3001,12 @@ void Spell::cast(bool skipCheck) TakePower(); TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot } + else if (Item* targetItem = m_targets.getItemTarget()) + { + /// Not own traded item (in trader trade slot) req. reagents including triggered spell case + if (targetItem->GetOwnerGUID() != m_caster->GetGUID()) + TakeReagents(); + } // are there any spells need to be triggered after hit? // handle SPELL_AURA_ADD_TARGET_TRIGGER auras @@ -4330,7 +4372,12 @@ void Spell::TakeRunePower() void Spell::TakeReagents() { if (m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed. - return; + { + Item* targetItem = m_targets.getItemTarget(); + /// Not own traded item (in trader trade slot) req. reagents including triggered spell case + if (!(targetItem && targetItem->GetOwnerGUID() != m_caster->GetGUID())) + return; + } if (m_caster->GetTypeId() != TYPEID_PLAYER) return; @@ -5430,6 +5477,26 @@ SpellCastResult Spell::CheckCast(bool strict) } } + // check trade slot case (last, for allow catch any another cast problems) + if (m_targets.getTargetMask() & TARGET_FLAG_TRADE_ITEM) + { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_NOT_TRADING; + + TradeData* my_trade = m_caster->ToPlayer()->GetTradeData(); + + if (!my_trade) + return SPELL_FAILED_NOT_TRADING; + + TradeSlots slot = TradeSlots(m_targets.getItemTargetGUID()); + if (slot != TRADE_SLOT_NONTRADED) + return SPELL_FAILED_ITEM_NOT_READY; + + if (!m_IsTriggeredSpell) + if (my_trade->GetSpell()) + return SPELL_FAILED_ITEM_ALREADY_ENCHANTED; + } + // all ok return SPELL_CAST_OK; } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 372c668d2de..7b6597720e1 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -180,6 +180,7 @@ class SpellCastTargets Item* getItemTarget() const { return m_itemTarget; } uint32 getItemTargetEntry() const { return m_itemTargetEntry; } void setItemTarget(Item* item); + void setTradeItemTarget(Player* caster); void updateTradeSlotItem() { if (m_itemTarget && (m_targetMask & TARGET_FLAG_TRADE_ITEM)) |
