2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
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
20 #include "Database/DatabaseEnv.h"
21 #include "Database/SQLStorage.h"
22 #include "ProgressBar.h"
24 #include "AccountMgr.h"
25 #include "AuctionHouseMgr.h"
29 #include "ObjectMgr.h"
32 #include "WorldSession.h"
34 #include "Policies/SingletonImp.h"
36 INSTANTIATE_SINGLETON_1( AuctionHouseMgr
);
39 AuctionHouseMgr::AuctionHouseMgr()
43 AuctionHouseMgr::~AuctionHouseMgr()
45 for(ItemMap::iterator itr
= mAitems
.begin(); itr
!= mAitems
.end(); ++itr
)
49 AuctionHouseObject
* AuctionHouseMgr::GetAuctionsMap( AuctionLocation location
)
54 return & mHordeAuctions
;
56 case AUCTION_ALLIANCE
:
57 return & mAllianceAuctions
;
60 return & mNeutralAuctions
;
64 uint32
AuctionHouseMgr::GetAuctionCut(AuctionLocation location
, uint32 highBid
)
66 if (location
== AUCTION_NEUTRAL
&& !sWorld
.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION
))
67 return (uint32
) (0.15f
* highBid
* sWorld
.getRate(RATE_AUCTION_CUT
));
69 return (uint32
) (0.05f
* highBid
* sWorld
.getRate(RATE_AUCTION_CUT
));
72 uint32
AuctionHouseMgr::GetAuctionDeposit(AuctionLocation location
, uint32 time
, Item
*pItem
)
74 float percentance
; // in 0..1
75 if (location
== AUCTION_NEUTRAL
&& !sWorld
.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION
))
80 percentance
*= sWorld
.getRate(RATE_AUCTION_DEPOSIT
);
82 return uint32( percentance
* pItem
->GetProto()->SellPrice
* pItem
->GetCount() * (time
/ MIN_AUCTION_TIME
) );
85 /// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
86 uint32
AuctionHouseMgr::GetAuctionOutBid(uint32 currentBid
)
88 uint32 outbid
= (currentBid
/ 100) * 5;
95 void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry
*auction
)
97 Item
*pItem
= GetAItem(auction
->item_guidlow
);
101 uint64 bidder_guid
= MAKE_NEW_GUID(auction
->bidder
, 0, HIGHGUID_PLAYER
);
102 Player
*bidder
= objmgr
.GetPlayer(bidder_guid
);
104 uint32 bidder_accId
= 0;
107 if( sWorld
.getConfig(CONFIG_GM_LOG_TRADE
) )
109 uint32 bidder_security
= 0;
110 std::string bidder_name
;
113 bidder_accId
= bidder
->GetSession()->GetAccountId();
114 bidder_security
= bidder
->GetSession()->GetSecurity();
115 bidder_name
= bidder
->GetName();
119 bidder_accId
= objmgr
.GetPlayerAccountIdByGUID(bidder_guid
);
120 bidder_security
= accmgr
.GetSecurity(bidder_accId
);
122 if(bidder_security
> SEC_PLAYER
) // not do redundant DB requests
124 if(!objmgr
.GetPlayerNameByGUID(bidder_guid
,bidder_name
))
125 bidder_name
= objmgr
.GetMangosStringForDBCLocale(LANG_UNKNOWN
);
129 if( bidder_security
> SEC_PLAYER
)
131 std::string owner_name
;
132 if(!objmgr
.GetPlayerNameByGUID(auction
->owner
,owner_name
))
133 owner_name
= objmgr
.GetMangosStringForDBCLocale(LANG_UNKNOWN
);
135 uint32 owner_accid
= objmgr
.GetPlayerAccountIdByGUID(auction
->owner
);
137 sLog
.outCommand(bidder_accId
,"GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)",
138 bidder_name
.c_str(),bidder_accId
,pItem
->GetProto()->Name1
,pItem
->GetEntry(),pItem
->GetCount(),auction
->bid
,owner_name
.c_str(),owner_accid
);
142 bidder_accId
= objmgr
.GetPlayerAccountIdByGUID(bidder_guid
);
145 if(bidder
|| bidder_accId
)
147 std::ostringstream msgAuctionWonSubject
;
148 msgAuctionWonSubject
<< auction
->item_template
<< ":0:" << AUCTION_WON
;
150 std::ostringstream msgAuctionWonBody
;
151 msgAuctionWonBody
.width(16);
152 msgAuctionWonBody
<< std::right
<< std::hex
<< auction
->owner
;
153 msgAuctionWonBody
<< std::dec
<< ":" << auction
->bid
<< ":" << auction
->buyout
;
154 sLog
.outDebug( "AuctionWon body string : %s", msgAuctionWonBody
.str().c_str() );
156 //prepare mail data... :
157 uint32 itemTextId
= objmgr
.CreateItemText( msgAuctionWonBody
.str() );
159 // set owner to bidder (to prevent delete item with sender char deleting)
160 // owner in `data` will set at mail receive and item extracting
161 CharacterDatabase
.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction
->bidder
,pItem
->GetGUIDLow());
162 CharacterDatabase
.CommitTransaction();
165 mi
.AddItem(auction
->item_guidlow
, auction
->item_template
, pItem
);
168 bidder
->GetSession()->SendAuctionBidderNotification( auction
->location
, auction
->Id
, bidder_guid
, 0, 0, auction
->item_template
);
170 RemoveAItem(pItem
->GetGUIDLow()); // we have to remove the item, before we delete it !!
172 // will delete item or place to receiver mail list
173 WorldSession::SendMailTo(bidder
, MAIL_AUCTION
, MAIL_STATIONERY_AUCTION
, auction
->location
, auction
->bidder
, msgAuctionWonSubject
.str(), itemTextId
, &mi
, 0, 0, MAIL_CHECK_MASK_AUCTION
);
175 // receiver not exist
178 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE guid='%u'", pItem
->GetGUIDLow());
179 RemoveAItem(pItem
->GetGUIDLow()); // we have to remove the item, before we delete it !!
184 void AuctionHouseMgr::SendAuctionSalePendingMail( AuctionEntry
* auction
)
186 uint64 owner_guid
= MAKE_NEW_GUID(auction
->owner
, 0, HIGHGUID_PLAYER
);
187 Player
*owner
= objmgr
.GetPlayer(owner_guid
);
189 // owner exist (online or offline)
190 if(owner
|| objmgr
.GetPlayerAccountIdByGUID(owner_guid
))
192 std::ostringstream msgAuctionSalePendingSubject
;
193 msgAuctionSalePendingSubject
<< auction
->item_template
<< ":0:" << AUCTION_SALE_PENDING
;
195 std::ostringstream msgAuctionSalePendingBody
;
196 uint32 auctionCut
= GetAuctionCut(auction
->location
, auction
->bid
);
198 time_t distrTime
= time(NULL
) + HOUR
;
200 msgAuctionSalePendingBody
.width(16);
201 msgAuctionSalePendingBody
<< std::right
<< std::hex
<< auction
->bidder
;
202 msgAuctionSalePendingBody
<< std::dec
<< ":" << auction
->bid
<< ":" << auction
->buyout
;
203 msgAuctionSalePendingBody
<< ":" << auction
->deposit
<< ":" << auctionCut
<< ":0:";
204 msgAuctionSalePendingBody
<< secsToTimeBitFields(distrTime
);
206 sLog
.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody
.str().c_str());
208 uint32 itemTextId
= objmgr
.CreateItemText( msgAuctionSalePendingBody
.str() );
210 WorldSession::SendMailTo(owner
, MAIL_AUCTION
, MAIL_STATIONERY_AUCTION
, auction
->location
, auction
->owner
, msgAuctionSalePendingSubject
.str(), itemTextId
, NULL
, 0, 0, MAIL_CHECK_MASK_AUCTION
);
214 //call this method to send mail to auction owner, when auction is successful, it does not clear ram
215 void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry
* auction
)
217 uint64 owner_guid
= MAKE_NEW_GUID(auction
->owner
, 0, HIGHGUID_PLAYER
);
218 Player
*owner
= objmgr
.GetPlayer(owner_guid
);
220 uint32 owner_accId
= 0;
222 owner_accId
= objmgr
.GetPlayerAccountIdByGUID(owner_guid
);
225 if(owner
|| owner_accId
)
227 std::ostringstream msgAuctionSuccessfulSubject
;
228 msgAuctionSuccessfulSubject
<< auction
->item_template
<< ":0:" << AUCTION_SUCCESSFUL
;
230 std::ostringstream auctionSuccessfulBody
;
231 uint32 auctionCut
= GetAuctionCut(auction
->location
, auction
->bid
);
233 auctionSuccessfulBody
.width(16);
234 auctionSuccessfulBody
<< std::right
<< std::hex
<< auction
->bidder
;
235 auctionSuccessfulBody
<< std::dec
<< ":" << auction
->bid
<< ":" << auction
->buyout
;
236 auctionSuccessfulBody
<< ":" << auction
->deposit
<< ":" << auctionCut
;
238 sLog
.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody
.str().c_str());
240 uint32 itemTextId
= objmgr
.CreateItemText( auctionSuccessfulBody
.str() );
242 uint32 profit
= auction
->bid
+ auction
->deposit
- auctionCut
;
246 //send auction owner notification, bidder must be current!
247 owner
->GetSession()->SendAuctionOwnerNotification( auction
);
250 WorldSession::SendMailTo(owner
, MAIL_AUCTION
, MAIL_STATIONERY_AUCTION
, auction
->location
, auction
->owner
, msgAuctionSuccessfulSubject
.str(), itemTextId
, NULL
, profit
, 0, MAIL_CHECK_MASK_AUCTION
, HOUR
);
255 void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry
* auction
)
256 { //return an item in auction to its owner by mail
257 Item
*pItem
= GetAItem(auction
->item_guidlow
);
260 sLog
.outError("Auction item (GUID: %u) not found, and lost.",auction
->item_guidlow
);
264 uint64 owner_guid
= MAKE_NEW_GUID(auction
->owner
, 0, HIGHGUID_PLAYER
);
265 Player
*owner
= objmgr
.GetPlayer(owner_guid
);
267 uint32 owner_accId
= 0;
269 owner_accId
= objmgr
.GetPlayerAccountIdByGUID(owner_guid
);
272 if(owner
|| owner_accId
)
274 std::ostringstream subject
;
275 subject
<< auction
->item_template
<< ":0:" << AUCTION_EXPIRED
;
278 owner
->GetSession()->SendAuctionOwnerNotification( auction
);
280 RemoveAItem(pItem
->GetGUIDLow()); // we have to remove the item, before we delete it !!
283 mi
.AddItem(auction
->item_guidlow
, auction
->item_template
, pItem
);
285 // will delete item or place to receiver mail list
286 WorldSession::SendMailTo(owner
, MAIL_AUCTION
, MAIL_STATIONERY_AUCTION
, auction
->location
, GUID_LOPART(owner_guid
), subject
.str(), 0, &mi
, 0, 0, MAIL_CHECK_MASK_NONE
);
291 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE guid='%u'",pItem
->GetGUIDLow());
292 RemoveAItem(pItem
->GetGUIDLow()); // we have to remove the item, before we delete it !!
297 void AuctionHouseMgr::LoadAuctionItems()
299 // data needs to be at first place for Item::LoadFromDB
300 QueryResult
*result
= CharacterDatabase
.Query( "SELECT data,itemguid,item_template FROM auctionhouse JOIN item_instance ON itemguid = guid" );
307 sLog
.outString(">> Loaded 0 auction items");
311 barGoLink
bar( result
->GetRowCount() );
320 fields
= result
->Fetch();
321 uint32 item_guid
= fields
[1].GetUInt32();
322 uint32 item_template
= fields
[2].GetUInt32();
324 ItemPrototype
const *proto
= objmgr
.GetItemPrototype(item_template
);
328 sLog
.outError( "ObjectMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid
,item_template
);
332 Item
*item
= NewItemOrBag(proto
);
334 if(!item
->LoadFromDB(item_guid
,0, result
))
343 while( result
->NextRow() );
347 sLog
.outString( ">> Loaded %u auction items", count
);
350 void AuctionHouseMgr::LoadAuctions()
352 QueryResult
*result
= CharacterDatabase
.Query("SELECT COUNT(*) FROM auctionhouse");
358 sLog
.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
362 Field
*fields
= result
->Fetch();
363 uint32 AuctionCount
=fields
[0].GetUInt32();
371 sLog
.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
375 result
= CharacterDatabase
.Query( "SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location FROM auctionhouse" );
381 sLog
.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
385 barGoLink
bar( AuctionCount
);
391 fields
= result
->Fetch();
395 aItem
= new AuctionEntry
;
396 aItem
->Id
= fields
[0].GetUInt32();
397 aItem
->auctioneer
= fields
[1].GetUInt32();
398 aItem
->item_guidlow
= fields
[2].GetUInt32();
399 aItem
->item_template
= fields
[3].GetUInt32();
400 aItem
->owner
= fields
[4].GetUInt32();
401 aItem
->buyout
= fields
[5].GetUInt32();
402 aItem
->time
= fields
[6].GetUInt32();
403 aItem
->bidder
= fields
[7].GetUInt32();
404 aItem
->bid
= fields
[8].GetUInt32();
405 aItem
->startbid
= fields
[9].GetUInt32();
406 aItem
->deposit
= fields
[10].GetUInt32();
408 uint32 loc
= fields
[11].GetUInt8();
409 if(!IsValidAuctionLocation(loc
))
411 CharacterDatabase
.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",aItem
->Id
);
412 sLog
.outError("Auction %u has wrong auction location (%u)", aItem
->Id
, loc
);
416 aItem
->location
= AuctionLocation(loc
);
418 // check if sold item exists for guid
419 // and item_template in fact (GetAItem will fail if problematic in result check in ObjectMgr::LoadAuctionItems)
420 if ( !GetAItem( aItem
->item_guidlow
) )
422 CharacterDatabase
.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",aItem
->Id
);
423 sLog
.outError("Auction %u has not a existing item : %u", aItem
->Id
, aItem
->item_guidlow
);
430 GetAuctionsMap( aItem
->location
)->AddAuction(aItem
);
432 } while (result
->NextRow());
436 sLog
.outString( ">> Loaded %u auctions", AuctionCount
);
439 void AuctionHouseMgr::AddAItem( Item
* it
)
442 ASSERT( mAitems
.find(it
->GetGUIDLow()) == mAitems
.end());
443 mAitems
[it
->GetGUIDLow()] = it
;
446 bool AuctionHouseMgr::RemoveAItem( uint32 id
)
448 ItemMap::iterator i
= mAitems
.find(id
);
449 if (i
== mAitems
.end())