From 03079ab9f724647d2a7519e15c7eeb8e477600b5 Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Fri, 11 Jun 2010 22:46:16 +0400 Subject: [PATCH] [10050] Implement apply non-trade slot item enchanting at trade complete. Base at original patch provided by arrai. Signed-off-by: VladimirMangos --- src/game/Player.cpp | 133 ++++++++++++++++++--- src/game/Player.h | 71 +++++++++--- src/game/SharedDefines.h | 27 +++++ src/game/Spell.cpp | 66 +++++++++-- src/game/Spell.h | 1 + src/game/SpellEffects.cpp | 12 -- src/game/SpellMgr.cpp | 16 ++- src/game/TradeHandler.cpp | 289 +++++++++++++++++++++++++++------------------- src/game/WorldSession.h | 4 +- src/shared/revision_nr.h | 2 +- 10 files changed, 438 insertions(+), 183 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index abc579f80..53706be37 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -302,6 +302,105 @@ bool SpellModifier::isAffectedOnSpell( SpellEntry const *spell ) const return false; } +//== TradeData ================================================= + +TradeData* TradeData::GetTraderData() const +{ + return m_trader->GetTradeData(); +} + +Item* TradeData::GetItem( TradeSlots slot ) const +{ + return !m_items[slot].IsEmpty() ? m_player->GetItemByGuid(m_items[slot]) : NULL; +} + +bool TradeData::HasItem( ObjectGuid item_guid ) const +{ + for(int i = 0; i < TRADE_SLOT_COUNT; ++i) + if (m_items[i] == item_guid) + return true; + return false; +} + + +Item* TradeData::GetSpellCastItem() const +{ + return !m_spellCastItem.IsEmpty() ? m_player->GetItemByGuid(m_spellCastItem) : NULL; +} + +void TradeData::SetItem( TradeSlots slot, Item* item ) +{ + ObjectGuid itemGuid = item ? item->GetObjectGuid() : ObjectGuid(); + + 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*/ ) +{ + ObjectGuid itemGuid = castItem ? castItem->GetObjectGuid() : ObjectGuid(); + + 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(); +} + +void TradeData::Update( bool for_trader /*= true*/ ) +{ + if (for_trader) + 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; @@ -9054,34 +9153,34 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const for(int 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 ) + if (tempcount >= count) return true; } } for(int 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 ) + if (tempcount >= count) return true; } } for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) { - if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { 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 ) + if (tempcount >= count) return true; } } @@ -9093,24 +9192,24 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const for(int 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 ) + if (tempcount >= count) return true; } } for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) { - if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { 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 ) + if (tempcount >= count) return true; } } @@ -11231,7 +11330,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq { if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if (pItem->GetEntry() == item) + if (pItem->GetEntry() == item && !pItem->IsInTrade()) { if (pItem->GetCount() + remcount <= count) { @@ -11259,7 +11358,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq { if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if (pItem->GetEntry() == item) + if (pItem->GetEntry() == item && !pItem->IsInTrade()) { if (pItem->GetCount() + remcount <= count) { @@ -11292,7 +11391,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq { 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) @@ -11323,7 +11422,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq { 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) { @@ -11992,7 +12091,7 @@ void Player::TradeCancel(bool sendback) { if (m_trade) { - Player* trader = m_trade->m_tradeWith; + Player* trader = m_trade->GetTrader(); // send yellow "Trade canceled" message to both traders if (sendback) diff --git a/src/game/Player.h b/src/game/Player.h index 9436552e9..bdaa39991 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1019,24 +1019,58 @@ struct BGData bool HasTaxiPath() const { return taxiPath[0] && taxiPath[1]; } }; -struct TradeData +class TradeData { - explicit TradeData(Player* tradeWith) - : m_tradeWith(tradeWith), m_acceptedTrade(false), m_tradeGold(0), m_tradeSpell(0) {} + 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* m_tradeWith; - bool m_acceptedTrade; - uint32 m_tradeGold; - uint32 m_tradeSpell; - ObjectGuid m_tradeItems[TRADE_SLOT_COUNT]; + public: // access functions - bool HasItem(ObjectGuid item_guid) const - { - for(int i = 0; i < TRADE_SLOT_COUNT; ++i) - if (m_tradeItems[i] == item_guid) - return true; - return false; - } + Player* GetTrader() const { return m_trader; } + TradeData* GetTraderData() const; + + Item* GetItem(TradeSlots slot) const; + bool HasItem(ObjectGuid item_guid) const; + + uint32 GetSpell() const { return m_spell; } + Item* GetSpellCastItem() const; + bool HasSpellCastItem() const { return !m_spellCastItem.IsEmpty(); } + + uint32 GetMoney() const { return m_money; } + + bool IsAccepted() const { return m_accepted; } + bool IsInAcceptProcess() const { return m_acceptProccess; } + public: // access functions + + void SetItem(TradeSlots slot, Item* item); + void SetSpell(uint32 spell_id, Item* castItem = NULL); + void SetMoney(uint32 money); + + void SetAccepted(bool state, bool crosssend = false); + + // must be called only from accept handler helper functions + 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 + ObjectGuid m_spellCastItem; // applied spell casted by item use + + ObjectGuid m_items[TRADE_SLOT_COUNT]; // traded itmes from m_player side including non-traded slot }; class MANGOS_DLL_SPEC Player : public Unit @@ -1203,7 +1237,7 @@ class MANGOS_DLL_SPEC Player : public Unit bool IsValidPos( uint8 bag, uint8 slot, bool explicit_pos ) const; uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, 2); } void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, 2, count); } - bool HasItemCount( uint32 item, uint32 count, bool inBankAlso = false ) const; + bool HasItemCount( uint32 item, uint32 count, bool inBankAlso = false) const; bool HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem = NULL); bool CanNoReagentCast(SpellEntry const* spellInfo) const; bool HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; @@ -1300,9 +1334,10 @@ class MANGOS_DLL_SPEC Player : public Unit bool BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot); float GetReputationPriceDiscount( Creature const* pCreature ) const; - Player* GetTrader() const { return m_trade ? m_trade->m_tradeWith : NULL; } + + Player* GetTrader() const { return m_trade ? m_trade->GetTrader() : NULL; } + TradeData* GetTradeData() const { return m_trade; } void TradeCancel(bool sendback); - Item* GetItemByTradeSlot(uint32 slot) const { return m_trade && !m_trade->m_tradeItems[slot].IsEmpty() ? GetItemByGuid(m_trade->m_tradeItems[slot]) : NULL; } void UpdateEnchantTime(uint32 time); void UpdateItemDuration(uint32 time, bool realtimeonly=false); diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 86e05e5e4..e7c3c4f8c 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -2670,6 +2670,33 @@ enum TotemSlot #define MAX_TOTEM_SLOT 4 +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). +}; + // we need to stick to 1 version or half of the stuff will work for someone // others will not and opposite // will only support WoW, WoW:TBC and WoW:WotLK 3.3.3 client build 11723... diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index af4d5d5d0..6597d88e4 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -176,6 +176,15 @@ void SpellCastTargets::setItemTarget(Item* item) m_targetMask |= TARGET_FLAG_ITEM; } +void SpellCastTargets::setTradeItemTarget(Player* caster) +{ + m_itemTargetGUID = ObjectGuid(uint64(TRADE_SLOT_NONTRADED)); + m_itemTargetEntry = 0; + m_targetMask |= TARGET_FLAG_TRADE_ITEM; + + Update(caster); +} + void SpellCastTargets::setCorpseTarget(Corpse* corpse) { m_CorpseTargetGUID = corpse->GetGUID(); @@ -189,17 +198,20 @@ void SpellCastTargets::Update(Unit* caster) NULL; m_itemTarget = NULL; - if(caster->GetTypeId() == TYPEID_PLAYER) + if (caster->GetTypeId() == TYPEID_PLAYER) { - if(m_targetMask & TARGET_FLAG_ITEM) - m_itemTarget = ((Player*)caster)->GetItemByGuid(m_itemTargetGUID); - else if(m_targetMask & TARGET_FLAG_TRADE_ITEM) + Player *player = ((Player*)caster); + + if (m_targetMask & TARGET_FLAG_ITEM) + m_itemTarget = player->GetItemByGuid(m_itemTargetGUID); + else if (m_targetMask & TARGET_FLAG_TRADE_ITEM) { - Player* pTrader = ((Player*)caster)->GetTrader(); - if(pTrader && m_itemTargetGUID.GetRawValue() < TRADE_SLOT_COUNT) - m_itemTarget = pTrader->GetItemByTradeSlot(uint32(m_itemTargetGUID.GetRawValue())); + if (TradeData* pTrade = player->GetTradeData()) + if (m_itemTargetGUID.GetRawValue() < TRADE_SLOT_COUNT) + m_itemTarget = pTrade->GetTraderData()->GetItem(TradeSlots(m_itemTargetGUID.GetRawValue())); } - if(m_itemTarget) + + if (m_itemTarget) m_itemTargetEntry = m_itemTarget->GetEntry(); } } @@ -5185,6 +5197,31 @@ SpellCastResult Spell::CheckCast(bool strict) } } + // check trade slot case (last, for allow catch any another cast problems) + if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) + { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_NOT_TRADING; + + Player *pCaster = ((Player*)m_caster); + TradeData* my_trade = pCaster->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 trade not complete then remember it in trade data + 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); + return SPELL_FAILED_DONT_REPORT; + } + } + // all ok return SPELL_CAST_OK; } @@ -5578,13 +5615,20 @@ SpellCastResult Spell::CheckPower() bool Spell::IgnoreItemRequirements() const { - if (m_IsTriggeredSpell) - return true; - /// Check if it's an enchant scroll. These have no required reagents even though their spell does. if (m_CastItem && m_CastItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_ENCHANT_SCROLL)) return true; + if (m_IsTriggeredSpell) + { + /// Not own traded item (in trader trade slot) req. reagents including triggered spell case + if (Item* targetItem = m_targets.getItemTarget()) + if (targetItem->GetOwnerGUID() != m_caster->GetGUID()) + return false; + + return true; + } + return false; } diff --git a/src/game/Spell.h b/src/game/Spell.h index ebd8c03e1..cea75be8b 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -170,6 +170,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)) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index e1db8016f..f0c6fcf44 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -4519,10 +4519,6 @@ void Spell::EffectEnchantItemPerm(SpellEffectIndex eff_idx) // add new enchanting if equipped item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true); - - // update trade window for show enchantment for caster in trade window - if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - p_caster->GetSession()->SendUpdateTrade(); } void Spell::EffectEnchantItemPrismatic(SpellEffectIndex eff_idx) @@ -4581,10 +4577,6 @@ void Spell::EffectEnchantItemPrismatic(SpellEffectIndex eff_idx) // add new enchanting if equipped item_owner->ApplyEnchantment(itemTarget,PRISMATIC_ENCHANTMENT_SLOT,true); - - // update trade window for show enchantment for caster in trade window - if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - p_caster->GetSession()->SendUpdateTrade(); } void Spell::EffectEnchantItemTmp(SpellEffectIndex eff_idx) @@ -4712,10 +4704,6 @@ void Spell::EffectEnchantItemTmp(SpellEffectIndex eff_idx) // add new enchanting if equipped item_owner->ApplyEnchantment(itemTarget, TEMP_ENCHANTMENT_SLOT, true); - - // update trade window for show enchantment for caster in trade window - if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - p_caster->GetSession()->SendUpdateTrade(); } void Spell::EffectTameCreature(SpellEffectIndex /*eff_idx*/) diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index bd59bf15c..a4c9651ae 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -63,9 +63,19 @@ int32 GetSpellMaxDuration(SpellEntry const *spellInfo) uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell) { - // some triggered spells have data only usable for client - if (spell && spell->IsTriggeredSpellWithRedundentData()) - return 0; + if (spell) + { + // some triggered spells have data only usable for client + if (spell->IsTriggeredSpellWithRedundentData()) + return 0; + + // spell targeted to non-trading trade slot item instant at trade success apply + if (spell->GetCaster()->GetTypeId()==TYPEID_PLAYER) + if (TradeData* my_trade = ((Player*)(spell->GetCaster()))->GetTradeData()) + if (Item* nonTrade = my_trade->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED)) + if (nonTrade == spell->m_targets.getItemTarget()) + return 0; + } SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex); diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp index cb1ae3859..5fc3ec4c0 100644 --- a/src/game/TradeHandler.cpp +++ b/src/game/TradeHandler.cpp @@ -25,37 +25,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; @@ -104,47 +78,23 @@ void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/) // recvPacket.print_storage(); } -void WorldSession::SendUpdateTrade() +void WorldSession::SendUpdateTrade(bool trader_state /*= true*/) { - if (!_player) - return; - - TradeData* my_trade = _player->m_trade; - if (!my_trade) - return; - - Player* trader = my_trade->m_tradeWith; - - TradeData* his_trade = trader->m_trade; - if (!his_trade) - return; - - // reset trade status - if (my_trade->m_acceptedTrade) - { - my_trade->m_acceptedTrade = false; - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } - - if (his_trade->m_acceptedTrade) - { - his_trade->m_acceptedTrade = false; - trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } + TradeData* view_trade = trader_state ? _player->GetTradeData()->GetTraderData() : _player->GetTradeData(); WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size - data << uint8(1); // can be different (only seen 0 and 1) + data << uint8(trader_state ? 1 : 0); // send trader or own trade windows state (last need for proper show spell apply to non-trade slot) 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(his_trade->m_tradeGold); // trader gold - data << uint32(his_trade->m_tradeSpell); // spell casted on lowest slot item + data << uint32(view_trade->GetMoney()); // trader gold + data << uint32(view_trade->GetSpell()); // spell casted on lowest slot item for(uint8 i = 0; i < TRADE_SLOT_COUNT; ++i) { data << uint8(i); // trade slot number, if not specified, then end of packet - if (Item* item = trader->GetItemByTradeSlot(i)) + if (Item* item = view_trade->GetItem(TradeSlots(i))) { data << uint32(item->GetProto()->ItemId); // entry data << uint32(item->GetProto()->DisplayInfoID);// display id @@ -181,10 +131,10 @@ void WorldSession::SendUpdateTrade() void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) { - if (!_player->m_trade) + Player* trader = _player->GetTrader(); + if (!trader) return; - Player* trader = _player->m_trade->m_tradeWith; for(int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { @@ -257,6 +207,48 @@ 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(int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) + { + if (Item* item = myTrade->GetItem(TradeSlots(i))) + { + DEBUG_LOG("player trade item %s bag: %u slot: %u", item->GetObjectGuid().GetString().c_str(), 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 %s bag: %u slot: %u", item->GetObjectGuid().GetString().c_str(), 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 (int 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) { @@ -266,7 +258,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) if (!my_trade) return; - Player* trader = my_trade->m_tradeWith; + Player* trader = my_trade->GetTrader(); TradeData* his_trade = trader->m_trade; if (!his_trade) @@ -276,28 +268,29 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; bool myCanCompleteTrade=true,hisCanCompleteTrade=true; + // 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 (my_trade->m_tradeGold > _player->GetMoney()) + if (my_trade->GetMoney() > _player->GetMoney()) { SendNotification(LANG_NOT_ENOUGH_GOLD); - trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - my_trade->m_acceptedTrade = false; + my_trade->SetAccepted(false, true); return; } // not accept case incorrect money amount - if (his_trade->m_tradeGold > trader->GetMoney()) + if (his_trade->GetMoney() > trader->GetMoney()) { trader->GetSession( )->SendNotification(LANG_NOT_ENOUGH_GOLD); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - his_trade->m_acceptedTrade = false; + 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) { - if (Item* item = _player->GetItemByTradeSlot(i)) + if (Item* item = my_trade->GetItem(TradeSlots(i))) { if (!item->CanBeTraded()) { @@ -306,7 +299,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) } } - if (Item* item = trader->GetItemByTradeSlot(i)) + if (Item* item = his_trade->GetItem(TradeSlots(i))) { if (!item->CanBeTraded()) { @@ -316,59 +309,117 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) } } - my_trade->m_acceptedTrade = true; - if (his_trade->m_acceptedTrade) + if (his_trade->IsAccepted()) { - // inform partner client - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + setAcceptTradeMode(my_trade, his_trade, myItems, hisItems); - // store items in local list and set 'in-trade' flag - for(int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) + Spell* my_spell = NULL; + SpellCastTargets my_targets; + + Spell* his_spell = NULL; + SpellCastTargets his_targets; + + // not accept if spell can't be casted now (cheating) + if (uint32 my_spell_id = my_trade->GetSpell()) + { + 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) + { + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + + my_trade->SetSpell(0); + return; + } + + 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) + { + my_spell->SendCastResult(res); + + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + + delete my_spell; + my_trade->SetSpell(0); + return; + } + } + + // not accept if spell can't be casted now (cheating) + if (uint32 his_spell_id = his_trade->GetSpell()) { - if (Item* item = _player->GetItemByTradeSlot(i)) + 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) { - DEBUG_LOG("player trade item %s bag: %u slot: %u", item->GetObjectGuid().GetString().c_str(), item->GetBagSlot(), item->GetSlot()); - //Can return NULL - myItems[i] = item; - myItems[i]->SetInTrade(); + delete my_spell; + his_trade->SetSpell(0); + + clearAcceptTradeMode(my_trade, his_trade); + clearAcceptTradeMode(myItems, hisItems); + return; } - if (Item* item = trader->GetItemByTradeSlot(i)) + 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) { - DEBUG_LOG("partner trade item %s bag: %u slot: %u", item->GetObjectGuid().GetString().c_str(), item->GetBagSlot(), item->GetSlot()); - hisItems[i] = item; - hisItems[i]->SetInTrade(); + 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); - // clear 'in-trade' flag - for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) - { - if (myItems[i]) - myItems[i]->SetInTrade(false); - if (hisItems[i]) - hisItems[i]->SetInTrade(false); - } + clearAcceptTradeMode(myItems, hisItems); // in case of missing space report error if(!myCanCompleteTrade) { + clearAcceptTradeMode(my_trade, his_trade); + SendNotification(LANG_NOT_FREE_TRADE_SLOTS); trader->GetSession( )->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + my_trade->SetAccepted(false); + his_trade->SetAccepted(false); return; } else if (!hisCanCompleteTrade) { + clearAcceptTradeMode(my_trade, his_trade); + SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS); - SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + my_trade->SetAccepted(false); + his_trade->SetAccepted(false); return; } @@ -393,29 +444,36 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) // logging money if (sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { - if (_player->GetSession()->GetSecurity() > SEC_PLAYER && my_trade->m_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(), - my_trade->m_tradeGold, + my_trade->GetMoney(), trader->GetName(), trader->GetSession()->GetAccountId()); } - if (trader->GetSession()->GetSecurity() > SEC_PLAYER && his_trade->m_tradeGold > 0) + if (trader->GetSession()->GetSecurity() > SEC_PLAYER && his_trade->GetMoney() > 0) { 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->m_tradeGold, + his_trade->GetMoney(), _player->GetName(),_player->GetSession()->GetAccountId()); } } // update money - _player->ModifyMoney( -int32(my_trade->m_tradeGold) ); - _player->ModifyMoney(his_trade->m_tradeGold ); - trader->ModifyMoney( -int32(his_trade->m_tradeGold) ); - trader->ModifyMoney(my_trade->m_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); // cleanup + clearAcceptTradeMode(my_trade, his_trade); delete _player->m_trade; _player->m_trade = NULL; delete trader->m_trade; @@ -442,8 +500,7 @@ void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/) if (!my_trade) return; - my_trade->m_tradeWith->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - my_trade->m_acceptedTrade = false; + my_trade->SetAccepted(false, true); } void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) @@ -452,7 +509,7 @@ void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) if (!my_trade) return; - my_trade->m_tradeWith->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); + my_trade->GetTrader()->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); } @@ -560,8 +617,8 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) } // OK start trade - _player->m_trade = new TradeData(pOther); - pOther->m_trade = new TradeData(_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; @@ -575,14 +632,12 @@ void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket) recvPacket >> gold; - TradeData* my_trade = _player->m_trade; + TradeData* my_trade = _player->GetTradeData(); if (!my_trade) return; // gold can be incorrect, but this is checked at trade finished. - my_trade->m_tradeGold = gold; - - my_trade->m_tradeWith->GetSession()->SendUpdateTrade(); + my_trade->SetMoney(gold); } void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) @@ -623,9 +678,7 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) return; } - my_trade->m_tradeItems[tradeSlot] = item->GetObjectGuid(); - - my_trade->m_tradeWith->GetSession()->SendUpdateTrade(); + my_trade->SetItem(TradeSlots(tradeSlot), item); } void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket) @@ -641,7 +694,5 @@ void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket) if (tradeSlot >= TRADE_SLOT_COUNT) return; - my_trade->m_tradeItems[tradeSlot].Clear(); - - my_trade->m_tradeWith->GetSession()->SendUpdateTrade(); + my_trade->SetItem(TradeSlots(tradeSlot), NULL); } diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 5a809d801..8aa6ba959 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -222,12 +222,12 @@ class MANGOS_DLL_SPEC 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_state = true); //pet void SendPetNameQuery(uint64 guid, uint32 petnumber); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index f4d853bea..03840b380 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10049" + #define REVISION_NR "10050" #endif // __REVISION_NR_H__ -- 2.11.4.GIT