2 * Copyright (C) 2005,2006 MaNGOS <http://www.mangosproject.org/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "WorldPacket.h"
20 #include "WorldSession.h"
24 #include "ObjectMgr.h"
26 #include "UpdateMask.h"
27 #include "AuctionHouseObject.h"
29 //pls DO NOT use iterator++, because it is slowlier than ++iterator!!!
30 //post-incrementation is always slowlier than pre-incrementation !
32 void WorldSession::HandleAuctionHelloOpcode( WorldPacket
& recv_data
)
34 uint64 guid
; //NPC guid
37 Creature
*unit
= ObjectAccessor::Instance().GetCreature(*_player
, guid
);
40 sLog
.outDebug( "WORLD: HandleAuctionHelloOpcode - NO SUCH UNIT! (GUID: %u)", uint32(GUID_LOPART(guid
)) );
43 if( unit
->IsHostileTo(_player
)) // do not talk with enemies
45 if( !unit
->isAuctioner()) // it's not auctioner
48 SendAuctionHello(guid
, unit
);
51 static uint8
AuctioneerFactionToLocation(uint32 faction
)
65 default: /* 85 and so on ... neutral*/
70 void WorldSession::SendAuctionHello( uint64 guid
, Creature
* unit
)
73 data
.Initialize( MSG_AUCTION_HELLO
);
74 data
<< (uint64
) guid
;
75 data
<< (uint32
) AuctioneerFactionToLocation(unit
->getFaction());
79 //this function inserts to WorldPacket auction's data
80 bool WorldSession::SendAuctionInfo(WorldPacket
& data
, AuctionEntry
* auction
)
82 Item
*pItem
= objmgr
.GetAItem(auction
->item_guid
);
85 sLog
.outError("auction to item, that doesn't exist !!!!");
89 data
<< pItem
->GetUInt32Value(OBJECT_FIELD_ENTRY
);
90 data
<< (uint32
) 0; //0 - HighBidder, 1 - outbid, BID TYPE - not sure
91 data
<< (uint32
) 0; //unknown constant 0 ?
92 data
<< (uint32
) 0; //not pItem->GetCreator();// 4a d0 64 02, 0, unknown
93 data
<< (uint32
) pItem
->GetCount(); //item->count
94 //item->charge FFFFFFF
95 data
<< (uint32
) pItem
->GetUInt32Value(ITEM_FIELD_SPELL_CHARGES
);
96 data
<< (uint32
) auction
->owner
; //Auction->owner
97 data
<< (uint32
) 0; //player_high_guid
98 data
<< (uint32
) auction
->startbid
; //Auction->startbid
99 data
<< (uint32
) 0; //minimal outbid... not fixed now... wait a moment ;-)
100 data
<< (uint32
) auction
->buyout
; //auction->buyout
101 data
<< (uint32
) (auction
->time
- time(NULL
)) * 1000; //time
102 data
<< (uint32
) auction
->bidder
; //auction->bidder current
103 data
<< (uint32
) 0; //0 ? .. player highguid
104 data
<< (uint32
) auction
->bid
; //current bid
108 //call this method when player bids, creates, or deletes auction
109 void WorldSession::SendAuctionCommandResult(uint32 auctionId
, uint32 Action
, uint32 ErrorCode
, uint32 bidError
)
112 data
.Initialize( SMSG_AUCTION_COMMAND_RESULT
);
117 data
<< bidError
; //when bid, then send 0, once...
121 //this function sends notification, if bidder is online
122 void WorldSession::SendAuctionBidderNotification( uint32 location
, uint32 auctionId
, uint64 bidder
, uint32 bidSum
, uint32 diff
, uint32 item_template
)
125 data
.Initialize(SMSG_AUCTION_BIDDER_NOTIFICATION
);
128 data
<< (uint64
) bidder
;
130 data
<< (uint32
) diff
;
131 data
<< item_template
;
136 void WorldSession::SendAuctionOwnerNotification( AuctionEntry
* auction
)
139 data
.Initialize(SMSG_AUCTION_OWNER_NOTIFICATION
);
141 data
<< auction
->bid
;
142 data
<< (uint32
) 0; //unk
143 data
<< (uint32
) 0; //unk
144 data
<< (uint32
) 0; //unk
145 data
<< auction
->item_template
;
146 data
<< (uint32
) 0; //unk
150 //this function sends mail to old bidder
151 void WorldSession::SendAuctionOutbiddedMail(AuctionEntry
*auction
, uint32 newPrice
)
153 uint32 mailId
= objmgr
.GenerateMailID();
154 time_t etime
= time(NULL
) + (30 * DAY
);
156 std::ostringstream msgAuctionExpiredSubject
;
157 msgAuctionExpiredSubject
<< auction
->item_template
<< ":0:" << AUCTION_OUTBIDDED
;
159 Player
*oldBidder
= objmgr
.GetPlayer((uint64
) auction
->bidder
);
162 oldBidder
->GetSession()->SendAuctionBidderNotification( auction
->location
, auction
->Id
, _player
->GetGUID(), newPrice
, newPrice
- auction
->bid
, auction
->item_template
);
163 oldBidder
->CreateMail(mailId
, AUCTIONHOUSE_MAIL
, auction
->location
, msgAuctionExpiredSubject
.str(), 0, 0, 0, etime
, auction
->bid
, 0, NOT_READ
, NULL
);
166 sDatabase
.PExecute("INSERT INTO `mail` (`id`,`messageType`,`sender`,`receiver`,`subject`,`itemPageId`,`item_guid`,`item_template`,`time`,`money`,`cod`,`checked`) "
167 "VALUES ('%u', '%d', '%u', '%u', '%s', '0', '0', '0', '" I64FMTD
"', '%u', '0', '%d')",
168 mailId
, AUCTIONHOUSE_MAIL
, auction
->location
, auction
->bidder
, msgAuctionExpiredSubject
.str().c_str(), (uint64
)etime
, auction
->bid
, NOT_READ
);
171 void WorldSession::HandleAuctionSellItem( WorldPacket
& recv_data
)
173 uint64 auctioneer
, item
;
174 uint32 etime
, bid
, buyout
;
175 recv_data
>> auctioneer
>> item
;
176 recv_data
>> bid
>> buyout
>> etime
;
177 Player
*pl
= GetPlayer();
179 Creature
*pCreature
= ObjectAccessor::Instance().GetCreature(*_player
, auctioneer
);
180 if(!pCreature
||!pCreature
->isAuctioner())
185 uint16 pos
= pl
->GetPosByGuid(item
);
186 Item
*it
= pl
->GetItemByPos( pos
);
188 // prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction)
189 if(!it
|| !it
->CanBeTraded())
191 //5b 02 00 00 00 00 00 00 00 00 02 00 00 00 -- -- : [.............
193 SendAuctionCommandResult(0, AUCTION_SELL_ITEM
, AUCTION_INTERNAL_ERROR
);
197 uint32 location
= AuctioneerFactionToLocation(pCreature
->getFaction());
198 AuctionHouseObject
* mAuctions
;
199 mAuctions
= objmgr
.GetAuctionsMap( location
);
201 //we have to take deposit :
202 uint32 deposit
= objmgr
.GetAuctionDeposit( location
, etime
, it
);
203 if ( pl
->GetMoney() < deposit
)
205 //SendAuctionCommandResult(0, AUCTION_SELL_ITEM, some error code , maybe 1?);
208 pl
->ModifyMoney( ((int32
) deposit
) * -1 );
210 AuctionEntry
*AH
= new AuctionEntry
;
211 AH
->Id
= objmgr
.GenerateAuctionID();
212 AH
->auctioneer
= GUID_LOPART(auctioneer
);
213 AH
->item_guid
= GUID_LOPART(item
);
214 AH
->item_template
= it
->GetEntry();
215 AH
->owner
= pl
->GetGUIDLow();
220 time_t base
= time(NULL
);
221 AH
->time
= ((time_t)(etime
* 60)) + base
;
222 AH
->deposit
= deposit
;
223 AH
->location
= location
;
225 sLog
.outDetail("selling item %u to auctioneer %u with inital bid %u with buyout %u and with time %u (in minutes) in location: %u", GUID_LOPART(item
), GUID_LOPART(auctioneer
), bid
, buyout
, GUID_LOPART(time
), location
);
226 mAuctions
->AddAuction(AH
);
228 // DB can have outdate auction item with same guid
229 objmgr
.RemoveAItem(GUID_LOPART(item
));
232 pl
->RemoveItem( (pos
>> 8),(pos
& 255), true);
233 it
->RemoveFromUpdateQueueOf(pl
);
234 it
->DeleteFromInventoryDB();
236 sDatabase
.PExecute("INSERT INTO `auctionhouse` (`id`,`auctioneerguid`,`itemguid`,`item_template`,`itemowner`,`buyoutprice`,`time`,`buyguid`,`lastbid`,`startbid`,`deposit`,`location`) "
237 "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" I64FMTD
"', '%u', '%u', '%u', '%u', '%u')",
238 AH
->Id
, AH
->auctioneer
, AH
->item_guid
, AH
->item_template
, AH
->owner
, AH
->buyout
, AH
->time
, AH
->bidder
, AH
->bid
, AH
->startbid
, AH
->deposit
, AH
->location
);
240 SendAuctionCommandResult(AH
->Id
, AUCTION_SELL_ITEM
, AUCTION_OK
);
241 //pl->SaveToDB() - isn't needed, because item will be removed from inventory now, only money are problem
244 void WorldSession::HandleAuctionPlaceBid( WorldPacket
& recv_data
)
250 recv_data
>> auctioneer
;
251 recv_data
>> auctionId
>> price
;
253 Creature
*pCreature
= ObjectAccessor::Instance().GetCreature(*_player
, auctioneer
);
254 if(!pCreature
||!pCreature
->isAuctioner())
256 uint32 location
= AuctioneerFactionToLocation(pCreature
->getFaction());
258 AuctionHouseObject
* mAuctions
;
259 mAuctions
= objmgr
.GetAuctionsMap( location
);
261 AuctionEntry
*auction
= mAuctions
->GetAuction(auctionId
);
262 Player
*pl
= GetPlayer();
263 if ((auction
) && (auction
->owner
!= pl
->GetGUIDLow()))
265 if (price
< auction
->bid
)
267 //auction has already higher bid, client tests it!
268 //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
271 if (price
> pl
->GetMoney())
273 //you don't have enought money!, client tests!
274 //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
277 if ((price
< auction
->buyout
) || (auction
->buyout
== 0))
279 if (auction
->bidder
> 0)
281 if ( auction
->bidder
== pl
->GetGUIDLow() )
283 pl
->ModifyMoney(((uint32
)(price
- auction
->bid
)) * -1);
287 // mail to last bidder if there's one... + return money
288 SendAuctionOutbiddedMail( auction
, price
);
289 pl
->ModifyMoney(((int32
) price
) * -1);
294 pl
->ModifyMoney(((int32
) price
) * -1);
296 auction
->bidder
= pl
->GetGUIDLow();
297 auction
->bid
= price
;
299 // after this update we should save player's money ...
300 sDatabase
.PExecute("UPDATE `auctionhouse` SET `buyguid` = '%u',`lastbid` = '%u' WHERE `id` = '%u';", auction
->bidder
, auction
->bid
, auction
->Id
);
302 SendAuctionCommandResult(auction
->Id
, AUCTION_PLACE_BID
, AUCTION_OK
, 0 );
307 if (pl
->GetGUIDLow() == auction
->bidder
)
309 pl
->ModifyMoney(-int32(auction
->buyout
- auction
->bid
));
313 pl
->ModifyMoney(-int32(auction
->buyout
));
314 if ( auction
->bidder
) //buyout for bidded auction ..
316 SendAuctionOutbiddedMail( auction
, auction
->buyout
);
319 auction
->bidder
= pl
->GetGUIDLow();
320 auction
->bid
= auction
->buyout
;
322 objmgr
.SendAuctionSuccessfulMail( auction
);
323 objmgr
.SendAuctionWonMail( auction
);
325 SendAuctionCommandResult(auction
->Id
, AUCTION_PLACE_BID
, AUCTION_OK
);
330 //send auction database error..., or somethig like that
332 SendAuctionCommandResult(auction
->Id
, AUCTION_PLACE_BID
, AUCTION_INTERNAL_ERROR
, 0);
336 //will be fixed soon , but now it's not used....
337 void WorldSession::HandleAuctionRemoveItem( WorldPacket
& recv_data
)
341 recv_data
>> auctioneer
;
342 recv_data
>> auctionID
;
343 sLog
.outDebug( "DELETE AUCTION AuctionID: %u", auctionID
);
344 // Fetches the AH auction
345 //AuctionEntry *ah = objmgr.GetAuction(auctionID);
346 /*Player *pl = GetPlayer();
348 if ((ah)) // && (ah->owner != pl->GetGUIDLow()))
350 Item *it = objmgr.GetAItem(ah->item_guidlow);
351 ItemPrototype const *proto = it->GetProto();
355 if (ah->bidder > 0) // If we have a bidder, we have to send him the money he paid
358 m->messageID = objmgr.GenerateMailID();
359 m->sender = ah->owner;
360 m->receiver = ah->bidder;
361 std::ostringstream msgAuctionCanceled;
362 msgAuctionCanceled << "Auction Was Canceled: " << proto->Name1;
363 m->subject = msgAuctionCanceled.str();
367 m->time = time(NULL) + (29 * DAY);
373 std::string subject = m->subject;
374 std::string body = m->body;
375 sDatabase.escape_string(body);
376 sDatabase.escape_string(subject);
378 sDatabase.PExecute("DELETE FROM `mail` WHERE `id` = '%u'", m->messageID);
379 sDatabase.PExecute("INSERT INTO `mail` (`id`,`sender`,`receiver`,`subject`,`body`,`item`,`item_template`,`time`,`money`,`cod`,`checked`) "
380 "VALUES( '%u', '%u', '%u', '%s', '%s', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u')",
381 m->messageID, m->sender, m->receiver, subject.c_str(), body.c_str(), m->item_guidlow, m->item_id, (uint64)m->time, m->money, m->COD, m->checked);
383 uint64 mrcpl = MAKE_GUID(m->receiver,HIGHGUID_PLAYER);
384 Player *mrpln = objmgr.GetPlayer(mrcpl);
391 Mail *mn2 = new Mail;
392 mn2->messageID = objmgr.GenerateMailID();
393 mn2->sender = ah->owner;
394 mn2->receiver = pl->GetGUIDLow();
395 std::ostringstream msgAuctionCanceledOwner;
396 msgAuctionCanceledOwner << "Auction Canceled: " << proto->Name1;
398 mn2->subject = msgAuctionCanceledOwner.str();
399 mn2->item_guidlow = ah->item_guidlow;
400 mn2->item_id = ah->item_id;
401 mn2->time = time(NULL) + (29 * DAY);
407 std::string subject = mn2->subject;
408 std::string body = mn2->body;
409 sDatabase.escape_string(body);
410 sDatabase.escape_string(subject);
412 sDatabase.PExecute("DELETE FROM `mail` WHERE `id` = '%u'", mn2->messageID);
413 sDatabase.PExecute("INSERT INTO `mail` (`id`,`sender`,`receiver`,`subject`,`body`,`item`,`item_template`,`time`,`money`,`cod`,`checked`) "
414 "VALUES( '%u', '%u', '%u', '%s', '%s', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u')",
415 mn2->messageID , mn2->sender , mn2->receiver , subject.c_str() , body.c_str(), mn2->item_guidlow , mn2->item_id , (uint64)mn2->time ,mn2->money ,mn2->COD ,mn2->checked);
417 uint64 mrcpl2 = MAKE_GUID(mn2->receiver,HIGHGUID_PLAYER);
418 Player *mrpln2 = objmgr.GetPlayer(mrcpl2);
422 mrpln2->AddMItem(it);
423 mrpln2->AddMail(mn2);
428 // TODO Item not found - Error handling
437 // Now remove the auction
438 sDatabase.PExecute("DELETE FROM `auctionhouse` WHERE `id` = '%u'",ah->Id);
439 objmgr.RemoveAItem(ah->item_guidlow);
440 objmgr.RemoveAuction(ah->Id);
442 // And return an updated list of items
444 data.Initialize( SMSG_AUCTION_OWNER_LIST_RESULT );
448 for (ObjectMgr::AuctionEntryMap::iterator itr = objmgr.GetAuctionsBegin();itr != objmgr.GetAuctionsEnd();itr++)
450 AuctionEntry *Aentry = itr->second;
453 if( Aentry->owner == _player->GetGUIDLow() )
455 Item *item = objmgr.GetAItem(Aentry->item_guidlow);
458 ItemPrototype const *proto = item->GetProto();
463 data << proto->ItemId;
467 data << uint32(item->GetCount());
469 data << item->GetOwnerGUID();
472 data << Aentry->buyout;
473 // May be need fixing
474 data << uint32((Aentry->time - time(NULL)) * 1000);
475 data << uint32(Aentry->bidder);
485 data.put<uint32>(0, count);
486 data << uint32(count);
489 // data << unk1 << auctionID; //need fix here, this code does nothing
490 // SendPacket(&data);*/
493 void WorldSession::HandleAuctionListBidderItems( WorldPacket
& recv_data
)
495 uint64 guid
; //NPC guid
496 float unknownAuction
; //0 Constant ?
499 recv_data
>> unknownAuction
;
501 Creature
*pCreature
= ObjectAccessor::Instance().GetCreature(*_player
, guid
);
502 if(!pCreature
||!pCreature
->isAuctioner())
504 uint32 location
= AuctioneerFactionToLocation(pCreature
->getFaction());
506 AuctionHouseObject
* mAuctions
;
507 mAuctions
= objmgr
.GetAuctionsMap( location
);
510 data
.Initialize( SMSG_AUCTION_BIDDER_LIST_RESULT
);
511 Player
*pl
= GetPlayer();
512 data
<< (uint32
) 0; //add 0 as count
514 for (AuctionHouseObject::AuctionEntryMap::iterator itr
= mAuctions
->GetAuctionsBegin();itr
!= mAuctions
->GetAuctionsEnd();++itr
)
516 AuctionEntry
*Aentry
= itr
->second
;
517 if( Aentry
&& Aentry
->bidder
== pl
->GetGUIDLow() && (cnt
< 51))
519 if (SendAuctionInfo(data
, itr
->second
))
523 data
.put( 0, cnt
); // add count to placeholder
524 data
<< cnt
; //not sure
528 void WorldSession::HandleAuctionListOwnerItems( WorldPacket
& recv_data
)
535 Creature
*pCreature
= ObjectAccessor::Instance().GetCreature(*_player
, guid
);
536 if(!pCreature
||!pCreature
->isAuctioner())
538 uint32 location
= AuctioneerFactionToLocation(pCreature
->getFaction());
540 AuctionHouseObject
* mAuctions
;
541 mAuctions
= objmgr
.GetAuctionsMap( location
);
544 data
.Initialize( SMSG_AUCTION_OWNER_LIST_RESULT
);
547 for (AuctionHouseObject::AuctionEntryMap::iterator itr
= mAuctions
->GetAuctionsBegin();itr
!= mAuctions
->GetAuctionsEnd();++itr
)
549 AuctionEntry
*Aentry
= itr
->second
;
552 if( Aentry
->owner
== _player
->GetGUIDLow() )
554 Item
*item
= objmgr
.GetAItem(Aentry
->item_guid
);
557 ItemPrototype
const *proto
= item
->GetProto();
560 if (SendAuctionInfo(data
, itr
->second
))
569 data
.put
<uint32
>(0, count
);
570 data
<< (uint32
) count
;
574 void WorldSession::HandleAuctionListItems( WorldPacket
& recv_data
)
576 std::string searchedname
, name
;
577 uint8 levelmin
, levelmax
, usable
, location
;
578 uint32 count
, totalcount
, listfrom
, auctionSlotID
, auctionMainCategory
, auctionSubCategory
, quality
;
582 recv_data
>> listfrom
;
583 recv_data
>> searchedname
;
584 recv_data
>> levelmin
>> levelmax
;
585 recv_data
>> auctionSlotID
>> auctionMainCategory
>> auctionSubCategory
;
586 recv_data
>> quality
>> usable
;
588 Creature
*pCreature
= ObjectAccessor::Instance().GetCreature(*_player
, guid
);
589 if(!pCreature
||!pCreature
->isAuctioner())
592 location
= AuctioneerFactionToLocation(pCreature
->getFaction());
593 AuctionHouseObject
* mAuctions
;
594 mAuctions
= objmgr
.GetAuctionsMap( location
);
596 sLog
.outDebug("Auctionhouse search guid: " I64FMTD
", list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", guid
, listfrom
, searchedname
.c_str(), levelmin
, levelmax
, auctionSlotID
, auctionMainCategory
, auctionSubCategory
, quality
, usable
);
599 data
.Initialize( SMSG_AUCTION_LIST_RESULT
);
603 for (AuctionHouseObject::AuctionEntryMap::iterator itr
= mAuctions
->GetAuctionsBegin();itr
!= mAuctions
->GetAuctionsEnd();++itr
)
605 AuctionEntry
*Aentry
= itr
->second
;
606 Item
*item
= objmgr
.GetAItem(Aentry
->item_guid
);
609 ItemPrototype
const *proto
= item
->GetProto();
612 if( auctionMainCategory
== (0xffffffff) || proto
->Class
== auctionMainCategory
)
614 if( auctionSubCategory
== (0xffffffff) || proto
->SubClass
== auctionSubCategory
)
616 if( auctionSlotID
== (0xffffffff) || proto
->InventoryType
== auctionSlotID
)
618 if( quality
== (0xffffffff) || proto
->Quality
== quality
)
620 if( usable
== (0x00) || _player
->CanUseItem( item
) == EQUIP_ERR_OK
)
622 if( ( levelmin
== (0x00) || proto
->RequiredLevel
>= levelmin
) && ( levelmax
== (0x00) || proto
->RequiredLevel
<= levelmax
) )
625 std::transform( name
.begin(), name
.end(), name
.begin(), ::tolower
);
626 std::transform( searchedname
.begin(), searchedname
.end(), searchedname
.begin(), ::tolower
);
627 if( searchedname
.empty() || name
.find( searchedname
) != std::string::npos
)
629 if ((count
< 50) && (totalcount
>= listfrom
))
632 SendAuctionInfo( data
, Aentry
);
645 data
.put
<uint32
>(0, count
);
646 data
<< (uint32
) totalcount
;