aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorwin32 <none@none>2009-12-16 18:58:36 +0200
committerwin32 <none@none>2009-12-16 18:58:36 +0200
commitbf2c3fd8cf32578324162a8bef2ac6e48e7e0009 (patch)
treeccb8f71a159aba2799cc009352a8626d95b6fc6c /src
parentdc53b4a9def06d012d791304aaba1c748a1f997a (diff)
MaNGOS merge: Mail System.
* Store loot in mail_loot_template indexed by mail template ids. * Implement proper creating mail text copy item from mail template based mail. * Move send functions to new MailDraft class from WorldSession. * Implement mails sending at player levelup. * Autor VladimirMangos, converted by me. --HG-- branch : trunk
Diffstat (limited to 'src')
-rw-r--r--src/game/AchievementMgr.cpp23
-rw-r--r--src/game/AuctionHouseHandler.cpp15
-rw-r--r--src/game/AuctionHouseMgr.cpp21
-rw-r--r--src/game/BattleGround.cpp8
-rw-r--r--src/game/Chat.cpp3
-rw-r--r--src/game/Chat.h3
-rw-r--r--src/game/DBCStructure.h2
-rw-r--r--src/game/DBCfmt.h2
-rw-r--r--src/game/GameObject.cpp6
-rw-r--r--src/game/Level1.cpp7
-rw-r--r--src/game/Level3.cpp37
-rw-r--r--src/game/LootMgr.cpp38
-rw-r--r--src/game/LootMgr.h8
-rw-r--r--src/game/Mail.cpp670
-rw-r--r--src/game/Mail.h164
-rw-r--r--src/game/ObjectMgr.cpp95
-rw-r--r--src/game/ObjectMgr.h29
-rw-r--r--src/game/Player.cpp107
-rw-r--r--src/game/Player.h4
-rw-r--r--src/game/World.cpp3
-rw-r--r--src/game/WorldSession.h8
21 files changed, 714 insertions, 539 deletions
diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp
index f041749cf08..5b8f0b6bb12 100644
--- a/src/game/AchievementMgr.cpp
+++ b/src/game/AchievementMgr.cpp
@@ -1708,16 +1708,6 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
{
Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
- MailItemsInfo mi;
- if(item)
- {
- // save new item before send
- item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
-
- // item
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
- }
-
int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
// subject and text
@@ -1736,7 +1726,18 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
uint32 itemTextId = objmgr.CreateItemText( text );
- WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft draft(subject, itemTextId);
+
+ if(item)
+ {
+ // save new item before send
+ item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
+
+ // item
+ draft.AddItem(item);
+ }
+
+ draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender));
}
}
diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp
index 9de5a98062c..12ed6fc797c 100644
--- a/src/game/AuctionHouseHandler.cpp
+++ b/src/game/AuctionHouseHandler.cpp
@@ -129,7 +129,9 @@ void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPri
if (oldBidder && _player)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
- WorldSession::SendMailTo(oldBidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->bidder, msgAuctionOutbiddedSubject.str(), 0, NULL, auction->bid, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(msgAuctionOutbiddedSubject.str())
+ .AddMoney(auction->bid)
+ .SendMailTo(MailReceiver(oldBidder, auction->bidder), auction);
}
}
@@ -149,7 +151,9 @@ void WorldSession::SendAuctionCancelledToBidderMail(AuctionEntry* auction)
std::ostringstream msgAuctionCancelledSubject;
msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER;
- WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->bidder, msgAuctionCancelledSubject.str(), 0, NULL, auction->bid, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(msgAuctionCancelledSubject.str())
+ .AddMoney(auction->bid)
+ .SendMailTo(MailReceiver(bidder, auction->bidder), auction);
}
}
@@ -442,11 +446,10 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
std::ostringstream msgAuctionCanceledOwner;
msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED;
- MailItemsInfo mi;
- mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
-
// item will deleted or added to received mail list
- WorldSession::SendMailTo(pl, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), pl->GetGUIDLow(), msgAuctionCanceledOwner.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(msgAuctionCanceledOwner.str())
+ .AddItem(pItem)
+ .SendMailTo(pl, auction);
}
else
{
diff --git a/src/game/AuctionHouseMgr.cpp b/src/game/AuctionHouseMgr.cpp
index a35109da461..dbade5b329d 100644
--- a/src/game/AuctionHouseMgr.cpp
+++ b/src/game/AuctionHouseMgr.cpp
@@ -160,13 +160,12 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
CharacterDatabase.CommitTransaction();
- MailItemsInfo mi;
- mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
-
if (bidder)
bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template);
- WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->bidder, msgAuctionWonSubject.str(), itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_AUCTION);
+ MailDraft(msgAuctionWonSubject.str(), itemTextId)
+ .AddItem(pItem)
+ .SendMailTo(MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_AUCTION);
}
}
@@ -196,7 +195,8 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction)
uint32 itemTextId = objmgr.CreateItemText(msgAuctionSalePendingBody.str());
- WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->owner, msgAuctionSalePendingSubject.str(), itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_AUCTION);
+ MailDraft(msgAuctionSalePendingSubject.str(), itemTextId)
+ .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_AUCTION);
}
}
@@ -233,7 +233,9 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction)
//send auction owner notification, bidder must be current!
owner->GetSession()->SendAuctionOwnerNotification(auction);
}
- WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->owner, msgAuctionSuccessfulSubject.str(), itemTextId, NULL, profit, 0, MAIL_CHECK_MASK_AUCTION, sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY));
+ MailDraft(msgAuctionSuccessfulSubject.str(), itemTextId)
+ .AddMoney(profit)
+ .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_AUCTION, HOUR);
}
}
@@ -256,10 +258,9 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction)
if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID())
owner->GetSession()->SendAuctionOwnerNotification(auction);
- MailItemsInfo mi;
- mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
-
- WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), GUID_LOPART(owner_guid), subject.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(subject.str())
+ .AddItem(pItem)
+ .SendMailTo(MailReceiver(owner,auction->owner), auction);
}
}
diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp
index e79290b5168..c6527584fd3 100644
--- a/src/game/BattleGround.cpp
+++ b/src/game/BattleGround.cpp
@@ -919,10 +919,6 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
// save new item before send
markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
- // item
- MailItemsInfo mi;
- mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
-
// subject: item name
std::string subject = markProto->Name1;
int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
@@ -937,7 +933,9 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
uint32 itemTextId = objmgr.CreateItemText( textBuf );
- WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(subject, itemTextId)
+ .AddItem(markItem)
+ .SendMailTo(plr, MailSender(MAIL_CREATURE, bmEntry));
}
}
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
index c8538a53afe..80f75ccffa6 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -469,6 +469,8 @@ ChatCommand * ChatHandler::getCommandTable()
{ "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL },
{ "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL },
// { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL },
+ { "mail_level_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMailLevelRewardCommand, "", NULL },
+ { "mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMailCommand, "", NULL },
{ "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL },
{ "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL },
{ "npc_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcOptionCommand, "", NULL },
@@ -480,7 +482,6 @@ ChatCommand * ChatHandler::getCommandTable()
{ "points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPointsOfInterestCommand, "",NULL},
{ "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL },
{ "quest_end_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL },
- { "quest_mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesQuestMailCommand, "", NULL },
{ "quest_start_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL },
{ "quest_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL },
{ "reference_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL },
diff --git a/src/game/Chat.h b/src/game/Chat.h
index 820fa5771a7..d0d9a6112fc 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -381,11 +381,12 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleReloadLootTemplatesFishingCommand(const char* args);
bool HandleReloadLootTemplatesGameobjectCommand(const char* args);
bool HandleReloadLootTemplatesItemCommand(const char* args);
+ bool HandleReloadLootTemplatesMailCommand(const char* args);
+ bool HandleReloadMailLevelRewardCommand(const char* args);
bool HandleReloadLootTemplatesMillingCommand(const char* args);
bool HandleReloadLootTemplatesPickpocketingCommand(const char* args);
bool HandleReloadLootTemplatesProspectingCommand(const char* args);
bool HandleReloadLootTemplatesReferenceCommand(const char* args);
- bool HandleReloadLootTemplatesQuestMailCommand(const char* args);
bool HandleReloadLootTemplatesSkinningCommand(const char* args);
bool HandleReloadLootTemplatesSpellCommand(const char* args);
bool HandleReloadTrinityStringCommand(const char* args);
diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h
index 8c60c28980e..a139c4efa51 100644
--- a/src/game/DBCStructure.h
+++ b/src/game/DBCStructure.h
@@ -1104,7 +1104,7 @@ struct MailTemplateEntry
uint32 ID; // 0
//char* subject[16]; // 1-16
// 17 name flags, unused
- //char* content[16]; // 18-33
+ char* content[16]; // 18-33
};
struct MapEntry
diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h
index 0e5fb8b83cf..2dcb7619a3e 100644
--- a/src/game/DBCfmt.h
+++ b/src/game/DBCfmt.h
@@ -74,7 +74,7 @@ const char ItemRandomPropertiesfmt[]="nxiiiiixxxxxxxxxxxxxxxxx";
const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiiiiiii";
const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii";
const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx";
-const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx";
const char MapEntryfmt[]="nxixssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiffiixxix";
const char MovieEntryfmt[]="nxx";
const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx";
diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp
index 42c2fd04cc4..02a3ede06aa 100644
--- a/src/game/GameObject.cpp
+++ b/src/game/GameObject.cpp
@@ -527,10 +527,8 @@ void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner)
GetZoneAndAreaId(zone,subzone);
// if subzone loot exist use it
- if(LootTemplates_Fishing.HaveLootFor(subzone))
- fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner,true);
- // else use zone loot
- else
+ if (!fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner, true, true))
+ // else use zone loot (must exist in like case)
fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner,true);
}
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp
index 49802dd3790..4ae90ebdcc3 100644
--- a/src/game/Level1.cpp
+++ b/src/game/Level1.cpp
@@ -2439,13 +2439,12 @@ bool ChatHandler::HandleSendMailCommand(const char* args)
std::string text = msgText;
// from console show not existed sender
- uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0;
+ MailSender sender(MAIL_NORMAL,m_session ? m_session->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM);
- uint32 messagetype = MAIL_NORMAL;
- uint32 stationery = MAIL_STATIONERY_GM;
uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
- WorldSession::SendMailTo(target,messagetype, stationery, sender_guidlo, GUID_LOPART(target_guid), subject, itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(subject, itemTextId)
+ .SendMailTo(MailReceiver(target,GUID_LOPART(target_guid)),sender);
std::string nameLink = playerLink(target_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
index 9b61f03e3f2..a095be0a531 100644
--- a/src/game/Level3.cpp
+++ b/src/game/Level3.cpp
@@ -559,6 +559,7 @@ bool ChatHandler::HandleReloadAllCommand(const char*)
HandleReloadAllLocalesCommand("");
HandleReloadAccessRequirementCommand("");
+ HandleReloadMailLevelRewardCommand("");
HandleReloadCommandCommand("");
HandleReloadReservedNameCommand("");
HandleReloadTrinityStringCommand("");
@@ -880,12 +881,12 @@ bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
return true;
}
-bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*)
+bool ChatHandler::HandleReloadLootTemplatesMailCommand(const char*)
{
- sLog.outString( "Re-Loading Loot Tables... (`quest_mail_loot_template`)" );
- LoadLootTemplates_QuestMail();
- LootTemplates_QuestMail.CheckLootRefs();
- SendGlobalGMSysMessage("DB table `quest_mail_loot_template` reloaded.");
+ sLog.outString( "Re-Loading Loot Tables... (`mail_loot_template`)" );
+ LoadLootTemplates_Mail();
+ LootTemplates_Mail.CheckLootRefs();
+ SendGlobalSysMessage("DB table `mail_loot_template` reloaded.");
return true;
}
@@ -1365,6 +1366,14 @@ bool ChatHandler::HandleReloadLocalesQuestCommand(const char* /*arg*/)
return true;
}
+bool ChatHandler::HandleReloadMailLevelRewardCommand(const char* /*arg*/)
+{
+ sLog.outString( "Re-Loading Player level dependent mail rewards..." );
+ objmgr.LoadMailLevelRewards();
+ SendGlobalSysMessage("DB table `mail_level_reward` reloaded.");
+ return true;
+}
+
bool ChatHandler::HandleLoadScriptsCommand(const char *args)
{
if(!LoadScriptingModule(args)) return true;
@@ -7093,25 +7102,23 @@ bool ChatHandler::HandleSendItemsCommand(const char *args)
}
// from console show not existed sender
- uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0;
+ MailSender sender(MAIL_NORMAL,m_session ? m_session->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM);
- uint32 messagetype = MAIL_NORMAL;
- uint32 stationery = MAIL_STATIONERY_GM;
uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
// fill mail
- MailItemsInfo mi; // item list preparing
+ MailDraft draft(subject, itemTextId);
for (ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr)
{
if(Item* item = Item::CreateItem(itr->first,itr->second,m_session ? m_session->GetPlayer() : 0))
{
item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ draft.AddItem(item);
}
}
- WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ draft.SendMailTo(MailReceiver(receiver,GUID_LOPART(receiver_guid)), sender);
std::string nameLink = playerLink(receiver_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
@@ -7155,13 +7162,13 @@ bool ChatHandler::HandleSendMoneyCommand(const char *args)
std::string text = msgText;
// from console show not existed sender
- uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0;
+ MailSender sender(MAIL_NORMAL,m_session ? m_session->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM);
- uint32 messagetype = MAIL_NORMAL;
- uint32 stationery = MAIL_STATIONERY_GM;
uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
- WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, money, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(subject, itemTextId)
+ .AddMoney(money)
+ .SendMailTo(MailReceiver(receiver,GUID_LOPART(receiver_guid)),sender);
std::string nameLink = playerLink(receiver_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp
index 6f131167c5e..2cfd92f11fc 100644
--- a/src/game/LootMgr.cpp
+++ b/src/game/LootMgr.cpp
@@ -42,10 +42,10 @@ LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenc
LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true);
LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true);
LootStore LootTemplates_Item( "item_loot_template", "item entry", true);
+LootStore LootTemplates_Mail( "mail_loot_template", "mail template id", false);
LootStore LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true);
LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true);
LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true);
-LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id (with mail template)",false);
LootStore LootTemplates_Reference( "reference_loot_template", "reference id", false);
LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true);
LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false);
@@ -388,18 +388,19 @@ void Loot::AddItem(LootStoreItem const & item)
}
// Calls processor of corresponding LootTemplate (which handles everything including references)
-void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal, uint16 lootMode /*= DEFAULT_LOOT_MODE*/)
+bool Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal, bool noEmptyError, uint16 lootMode /*= DEFAULT_LOOT_MODE*/)
{
// Must be provided
if (!loot_owner)
- return;
+ return false;
LootTemplate const* tab = store.GetLootFor(loot_id);
if (!tab)
{
- sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id);
- return;
+ if (!noEmptyError)
+ sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id);
+ return false;
}
items.reserve(MAX_NR_LOOT_ITEMS);
@@ -418,6 +419,8 @@ void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner,
// ... for personal loot
else
FillNotNormalLootFor(loot_owner);
+
+ return true;
}
void Loot::FillNotNormalLootFor(Player* pl)
@@ -1351,28 +1354,19 @@ void LoadLootTemplates_Prospecting()
LootTemplates_Prospecting.ReportUnusedIds(ids_set);
}
-void LoadLootTemplates_QuestMail()
+void LoadLootTemplates_Mail()
{
LootIdSet ids_set;
- LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set);
+ LootTemplates_Mail.LoadAndCollectLootIds(ids_set);
// remove real entries and check existence loot
- ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates();
- for (ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr)
- {
- if (!itr->second->GetRewMailTemplateId())
- continue;
-
- if (ids_set.count(itr->first))
- ids_set.erase(itr->first);
- /* disabled reporting: some quest mails not include items
- else
- LootTemplates_QuestMail.ReportNotExistedId(itr->first);
- */
- }
+ for(uint32 i = 1; i < sMailTemplateStore.GetNumRows(); ++i)
+ if(sMailTemplateStore.LookupEntry(i))
+ if(ids_set.count(i))
+ ids_set.erase(i);
// output error for any still listed (not referenced from appropriate table) ids
- LootTemplates_QuestMail.ReportUnusedIds(ids_set);
+ LootTemplates_Mail.ReportUnusedIds(ids_set);
}
void LoadLootTemplates_Skinning()
@@ -1449,7 +1443,7 @@ void LoadLootTemplates_Reference()
LootTemplates_Skinning.CheckLootRefs(&ids_set);
LootTemplates_Disenchant.CheckLootRefs(&ids_set);
LootTemplates_Prospecting.CheckLootRefs(&ids_set);
- LootTemplates_QuestMail.CheckLootRefs(&ids_set);
+ LootTemplates_Mail.CheckLootRefs(&ids_set);
LootTemplates_Reference.CheckLootRefs(&ids_set);
// output error for any still listed ids (not referenced from any loot table)
diff --git a/src/game/LootMgr.h b/src/game/LootMgr.h
index 2bb2b15f99b..bcd9b564904 100644
--- a/src/game/LootMgr.h
+++ b/src/game/LootMgr.h
@@ -288,7 +288,7 @@ struct Loot
void RemoveLooter(uint64 GUID) { PlayersLooting.erase(GUID); }
void generateMoneyLoot(uint32 minAmount, uint32 maxAmount);
- void FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal, uint16 lootMode = DEFAULT_LOOT_MODE);
+ bool FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal, bool noEmptyError = false, uint16 lootMode = DEFAULT_LOOT_MODE);
// Inserts the item into the loot (called by LootTemplate processors)
void AddItem(LootStoreItem const & item);
@@ -325,24 +325,24 @@ extern LootStore LootTemplates_Creature;
extern LootStore LootTemplates_Fishing;
extern LootStore LootTemplates_Gameobject;
extern LootStore LootTemplates_Item;
+extern LootStore LootTemplates_Mail;
extern LootStore LootTemplates_Milling;
extern LootStore LootTemplates_Pickpocketing;
extern LootStore LootTemplates_Skinning;
extern LootStore LootTemplates_Disenchant;
extern LootStore LootTemplates_Prospecting;
-extern LootStore LootTemplates_QuestMail;
extern LootStore LootTemplates_Spell;
void LoadLootTemplates_Creature();
void LoadLootTemplates_Fishing();
void LoadLootTemplates_Gameobject();
void LoadLootTemplates_Item();
+void LoadLootTemplates_Mail();
void LoadLootTemplates_Milling();
void LoadLootTemplates_Pickpocketing();
void LoadLootTemplates_Skinning();
void LoadLootTemplates_Disenchant();
void LoadLootTemplates_Prospecting();
-void LoadLootTemplates_QuestMail();
void LoadLootTemplates_Spell();
void LoadLootTemplates_Reference();
@@ -353,12 +353,12 @@ inline void LoadLootTables()
LoadLootTemplates_Fishing();
LoadLootTemplates_Gameobject();
LoadLootTemplates_Item();
+ LoadLootTemplates_Mail();
LoadLootTemplates_Milling();
LoadLootTemplates_Pickpocketing();
LoadLootTemplates_Skinning();
LoadLootTemplates_Disenchant();
LoadLootTemplates_Prospecting();
- LoadLootTemplates_QuestMail();
LoadLootTemplates_Spell();
LoadLootTemplates_Reference();
diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp
index 16f337136ea..ec775aa22a0 100644
--- a/src/game/Mail.cpp
+++ b/src/game/Mail.cpp
@@ -31,6 +31,9 @@
#include "Language.h"
#include "AuctionHouseBot.h"
#include "DBCStores.h"
+#include "BattleGroundMgr.h"
+#include "Item.h"
+#include "AuctionHouseMgr.h"
enum MailShowFlags
{
@@ -41,18 +44,6 @@ enum MailShowFlags
MAIL_SHOW_RETURN = 0x0010,
};
-void MailItem::deleteItem( bool inDB )
-{
- if(item)
- {
- if(inDB)
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow());
-
- delete item;
- item=NULL;
- }
-}
-
void WorldSession::HandleSendMail(WorldPacket & recv_data )
{
uint64 mailbox, unk3;
@@ -62,9 +53,6 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
recv_data >> mailbox;
recv_data >> receiver;
- if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
- return;
-
recv_data >> subject;
recv_data >> body;
@@ -72,34 +60,32 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
recv_data >> unk1; // stationery?
recv_data >> unk2; // 0x00000000
- MailItemsInfo mi;
-
uint8 items_count;
recv_data >> items_count; // attached items count
- if(items_count > 12) // client limit
+ if (items_count > MAX_MAIL_ITEMS) // client limit
{
GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
+ recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
return;
}
- if(items_count)
+ uint64 itemGUIDs[MAX_MAIL_ITEMS];
+
+ for (uint8 i = 0; i < items_count; ++i)
{
- for (uint8 i = 0; i < items_count; ++i)
- {
- uint8 item_slot;
- uint64 item_guid;
- recv_data >> item_slot;
- recv_data >> item_guid;
- mi.AddItem(GUID_LOPART(item_guid), item_slot);
- }
+ recv_data.read_skip<uint8>(); // item slot in mail, not used
+ recv_data >> itemGUIDs[i];
}
recv_data >> money >> COD; // money and cod
recv_data >> unk3; // const 0
recv_data >> unk4; // const 0
- items_count = mi.size(); // this is the real size after the duplicates have been removed
+ // packet read complete, now do check
+
+ if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
+ return;
if (receiver.empty())
return;
@@ -107,7 +93,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
Player* pl = _player;
uint64 rc = 0;
- if(normalizePlayerName(receiver))
+ if (normalizePlayerName(receiver))
rc = objmgr.GetPlayerGUIDByName(receiver);
if (!rc)
@@ -120,7 +106,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
- if(pl->GetGUID() == rc)
+ if (pl->GetGUID() == rc)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
return;
@@ -141,7 +127,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
uint32 rc_team = 0;
uint8 mails_count = 0; //do not allow to send to one player more than 100 mails
- if(receive)
+ if (receive)
{
rc_team = receive->GetTeam();
mails_count = receive->GetMailSize();
@@ -149,8 +135,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
else
{
rc_team = objmgr.GetPlayerTeamByGUID(rc);
- QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc));
- if(result)
+ if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)))
{
Field *fields = result->Fetch();
mails_count = fields[0].GetUInt32();
@@ -170,102 +155,96 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
return;
}
- uint32 rc_account = 0;
- if(receive)
- rc_account = receive->GetSession()->GetAccountId();
- else
- rc_account = objmgr.GetPlayerAccountIdByGUID(rc);
+ uint32 rc_account = receive
+ ? receive->GetSession()->GetAccountId()
+ : objmgr.GetPlayerAccountIdByGUID(rc);
+
+ Item* items[MAX_MAIL_ITEMS];
- if (items_count)
+ for (uint8 i = 0; i < items_count; ++i)
{
- for (MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
+ if (!itemGUIDs[i])
{
- MailItem& mailItem = mailItemIter->second;
+ pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
+ return;
+ }
- if(!mailItem.item_guidlow)
- {
- pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
- return;
- }
+ Item* item = pl->GetItemByGuid(itemGUIDs[i]);
- mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM));
- // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
- if(!mailItem.item)
- {
- pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
- return;
- }
+ // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
+ if (!item)
+ {
+ pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
+ return;
+ }
- if(!mailItem.item->CanBeTraded(true))
- {
- pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
- return;
- }
+ if (!item->CanBeTraded(true))
+ {
+ pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
+ return;
+ }
- if(mailItem.item->IsBoundAccountWide() && mailItem.item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
- {
- pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
- return;
- }
+ if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
+ {
+ pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
+ return;
+ }
- if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION))
- {
- pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
- return;
- }
+ if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION))
+ {
+ pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
+ return;
+ }
- if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
- {
- pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
- return;
- }
+ if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
+ {
+ pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
+ return;
}
+
+ items[i] = item;
}
+
pl->SendMailResult(0, MAIL_SEND, MAIL_OK);
- uint32 itemTextId = 0;
- if (!body.empty())
- {
- itemTextId = objmgr.CreateItemText( body );
- }
+ uint32 itemTextId = !body.empty() ? objmgr.CreateItemText( body ) : 0;
pl->ModifyMoney( -int32(reqmoney) );
pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost);
bool needItemDelay = false;
- if(items_count > 0 || money > 0)
+ MailDraft draft(subject, itemTextId);
+
+ if (items_count > 0 || money > 0)
{
if (items_count > 0)
{
- for (MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
+ for (uint8 i = 0; i < items_count; ++i)
{
- MailItem& mailItem = mailItemIter->second;
- if(!mailItem.item)
- continue;
-
- mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0;
-
- if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ Item* item = items[i];
+ if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)",
- GetPlayerName(), GetAccountId(), mailItem.item->GetProto()->Name1, mailItem.item->GetEntry(), mailItem.item->GetCount(), receiver.c_str(), rc_account);
+ GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account);
}
- pl->MoveItemFromInventory(mailItem.item->GetBagSlot(), mailItem.item->GetSlot(), true);
+ pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true);
CharacterDatabase.BeginTransaction();
- mailItem.item->DeleteFromInventoryDB(); //deletes item from character's inventory
- mailItem.item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
+ item->DeleteFromInventoryDB(); // deletes item from character's inventory
+ item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
// owner in data will set at mail receive and item extracting
- CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), mailItem.item->GetGUIDLow());
+ CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), item->GetGUIDLow());
CharacterDatabase.CommitTransaction();
+
+ draft.AddItem(item);
}
// if item send to character at another account, then apply item delivery delay
needItemDelay = pl->GetSession()->GetAccountId() != rc_account;
}
- if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
+ if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)",
GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account);
@@ -276,7 +255,10 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
// will delete item or place to receiver mail list
- WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, pl->GetGUIDLow(), GUID_LOPART(rc), subject, itemTextId, &mi, money, COD, MAIL_CHECK_MASK_NONE, deliver_delay);
+ draft
+ .AddMoney(money)
+ .AddCOD(COD)
+ .SendMailTo(MailReceiver(receive, GUID_LOPART(rc)), pl, MAIL_CHECK_MASK_NONE, deliver_delay);
CharacterDatabase.BeginTransaction();
pl->SaveInventoryAndGoldToDB();
@@ -289,11 +271,11 @@ void WorldSession::HandleMailMarkAsRead(WorldPacket & recv_data )
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
+ recv_data >> mailId;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
- recv_data >> mailId;
Player *pl = _player;
Mail *m = pl->GetMail(mailId);
if (m)
@@ -314,6 +296,7 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data )
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
+ recv_data.read_skip<uint32>(); // mailTemplateId
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
@@ -321,7 +304,7 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data )
Mail *m = _player->GetMail(mailId);
Player* pl = _player;
pl->m_mailsUpdated = true;
- if(m)
+ if (m)
{
// delete shouldn't show up for COD mails
if (m->COD)
@@ -340,14 +323,15 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
+ recv_data >> mailId;
+ recv_data.read_skip<uint64>(); // original sender GUID for return to, not used
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
- recv_data >> mailId;
Player *pl = _player;
Mail *m = pl->GetMail(mailId);
- if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR);
return;
@@ -361,84 +345,38 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
CharacterDatabase.CommitTransaction();
pl->RemoveMail(mailId);
- MailItemsInfo mi;
-
- if(m->HasItems())
+ // send back only to players and simple drop for other cases
+ if (m->messageType == MAIL_NORMAL)
{
- for (std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
+ MailDraft draft(m->subject, m->itemTextId);
+ if (m->mailTemplateId)
+ draft = MailDraft(m->mailTemplateId, false); // items already included
+
+ if (m->HasItems())
{
- Item *item = pl->GetMItem(itr2->item_guid);
- if(item)
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
- else
+ for (std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
{
- //WTF?
- }
+ Item *item = pl->GetMItem(itr2->item_guid);
+ if (item)
+ draft.AddItem(item);
+ else
+ {
+ //WTF?
+ }
- pl->RemoveMItem(itr2->item_guid);
+ pl->RemoveMItem(itr2->item_guid);
+ }
}
- }
-
- if (m->sender == auctionbot.GetAHBplayerGUID())
- {
- SendReturnToSender(MAIL_CREATURE, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId);
- }
- else
- {
- SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId);
+ if (m->sender == auctionbot.GetAHBplayerGUID())
+ draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, auctionbot.GetAHBplayerGUID());
+ else
+ draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender);
}
delete m; //we can deallocate old mail
pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK);
}
-void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, const std::string& subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint16 mailTemplateId )
-{
- if(messageType != MAIL_NORMAL) // return only to players
- {
- mi->deleteIncludedItems(true);
- return;
- }
-
- Player *receiver = objmgr.GetPlayer(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
-
- uint32 rc_account = 0;
- if(!receiver)
- rc_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
-
- if(!receiver && !rc_account) // sender not exist
- {
- mi->deleteIncludedItems(true);
- return;
- }
-
- // prepare mail and send in other case
- bool needItemDelay = false;
-
- if(mi && !mi->empty())
- {
- // if item send to character at another account, then apply item delivery delay
- needItemDelay = sender_acc != rc_account;
-
- // set owner to new receiver (to prevent delete item with sender char deleting)
- CharacterDatabase.BeginTransaction();
- for (MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
- {
- MailItem& mailItem = mailItemIter->second;
- mailItem.item->SaveToDB(); // item not in inventory and can be save standalone
- // owner in data will set at mail receive and item extracting
- CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, mailItem.item->GetGUIDLow());
- }
- CharacterDatabase.CommitTransaction();
- }
-
- // If theres is an item, there is a one hour delivery delay.
- uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
-
- // will delete item or place to receiver mail list
- WorldSession::SendMailTo(receiver, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, sender_guid, receiver_guid, subject, itemTextId, mi, money, 0, MAIL_CHECK_MASK_RETURNED,deliver_delay,mailTemplateId);
-}
-
//called when player takes item attached in mail
void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
{
@@ -446,23 +384,23 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
uint32 mailId;
uint32 itemId;
recv_data >> mailbox;
+ recv_data >> mailId;
+ recv_data >> itemId; // item guid low
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
- recv_data >> mailId;
- recv_data >> itemId; // item guid low?
Player* pl = _player;
Mail* m = pl->GetMail(mailId);
- if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR);
return;
}
// prevent cheating with skip client money check
- if(pl->GetMoney() < m->COD)
+ if (pl->GetMoney() < m->COD)
{
pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY);
return;
@@ -472,7 +410,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
ItemPosCountVec dest;
uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, it, false );
- if( msg == EQUIP_ERR_OK )
+ if (msg == EQUIP_ERR_OK)
{
m->RemoveItem(itemId);
m->removedItems.push_back(itemId);
@@ -484,10 +422,10 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
uint32 sender_accId = 0;
- if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
std::string sender_name;
- if(receive)
+ if (receive)
{
sender_accId = receive->GetSession()->GetAccountId();
sender_name = receive->GetName();
@@ -497,19 +435,21 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
// can be calculated early
sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid);
- if(!objmgr.GetPlayerNameByGUID(sender_guid,sender_name))
+ if (!objmgr.GetPlayerNameByGUID(sender_guid,sender_name))
sender_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN);
}
sLog.outCommand(GetAccountId(),"GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)",
GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount(),m->COD,sender_name.c_str(),sender_accId);
}
- else if(!receive)
+ else if (!receive)
sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid);
// check player existence
- if(receive || sender_accId)
+ if (receive || sender_accId)
{
- WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, m->receiver, m->sender, m->subject, 0, NULL, m->COD, 0, MAIL_CHECK_MASK_COD_PAYMENT);
+ MailDraft(m->subject)
+ .AddMoney(m->COD)
+ .SendMailTo(MailReceiver(receive,m->sender),MailSender(MAIL_NORMAL,m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
}
pl->ModifyMoney( -int32(m->COD) );
@@ -546,7 +486,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data )
Player *pl = _player;
Mail* m = pl->GetMail(mailId);
- if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR);
return;
@@ -578,20 +518,24 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
Player* pl = _player;
//load players mails, and mailed items
- if(!pl->m_mailsLoaded)
+ if (!pl->m_mailsLoaded)
pl ->_LoadMail();
// client can't work with packets > max int16 value
const uint32 maxPacketSize = 32767;
- uint32 mails_count = 0; // real send to client mails amount
+ uint32 mailsCount = 0; // real send to client mails amount
WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size
data << uint8(0); // mail's count
time_t cur_time = time(NULL);
- for (PlayerMails::iterator itr = pl->GetmailBegin(); itr != pl->GetmailEnd(); ++itr)
+ for (PlayerMails::iterator itr = pl->GetMailBegin(); itr != pl->GetMailEnd(); ++itr)
{
+ // packet send mail count as uint8, prevent overflow
+ if (mailsCount >= 254)
+ break;
+
// skip deleted or not delivered (deliver delay not expired) mails
if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time)
continue;
@@ -600,7 +544,7 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
size_t next_mail_size = 2+4+1+8+4*8+((*itr)->subject.size()+1)+1+item_count*(1+4+4+6*3*4+4+4+1+4+4+4);
- if(data.wpos()+next_mail_size > maxPacketSize)
+ if (data.wpos()+next_mail_size > maxPacketSize)
break;
uint32 show_flags = 0;
@@ -611,9 +555,9 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
if ((*itr)->HasItems() && (*itr)->messageType == MAIL_NORMAL)
show_flags |= MAIL_SHOW_RETURN;
- data << (uint16) 0x0040; // unknown 2.3.0, different values
- data << (uint32) (*itr)->messageID; // Message ID
- data << (uint8) (*itr)->messageType; // Message Type
+ data << uint16(next_mail_size); // Message size
+ data << uint32((*itr)->messageID); // Message ID
+ data << uint8((*itr)->messageType); // Message Type
switch((*itr)->messageType)
{
@@ -623,63 +567,61 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
case MAIL_CREATURE:
case MAIL_GAMEOBJECT:
case MAIL_AUCTION:
- data << (uint32) (*itr)->sender; // creature/gameobject entry, auction id
+ data << uint32((*itr)->sender); // creature/gameobject entry, auction id
break;
case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI
break;
}
- data << (uint32) (*itr)->COD; // COD
- data << (uint32) (*itr)->itemTextId; // sure about this
- data << (uint32) 0; // unknown
- data << (uint32) (*itr)->stationery; // stationery (Stationery.dbc)
- data << (uint32) (*itr)->money; // Gold
- data << (uint32) show_flags; // unknown, 0x4 - auction, 0x10 - normal
- // Time
- data << (float) ((*itr)->expire_time-time(NULL))/DAY;
- data << (uint32) (*itr)->mailTemplateId; // mail template (MailTemplate.dbc)
- data << (*itr)->subject; // Subject string - once 00, when mail type = 3
-
- data << (uint8) item_count; // client limit is 0x10
+ data << uint32((*itr)->COD); // COD
+ data << uint32((*itr)->itemTextId); // sure about this
+ data << uint32(0); // unknown
+ data << uint32((*itr)->stationery); // stationery (Stationery.dbc)
+ data << uint32((*itr)->money); // Gold
+ data << uint32(show_flags); // unknown, 0x4 - auction, 0x10 - normal
+ data << float(((*itr)->expire_time-time(NULL))/DAY); // Time
+ data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc)
+ data << (*itr)->subject; // Subject string - once 00, when mail type = 3
+ data << uint8(item_count); // client limit is 0x10
for (uint8 i = 0; i < item_count; ++i)
{
Item *item = pl->GetMItem((*itr)->items[i].item_guid);
// item index (0-6?)
- data << (uint8) i;
+ data << uint8(i);
// item guid low?
- data << (uint32) (item ? item->GetGUIDLow() : 0);
+ data << uint32((item ? item->GetGUIDLow() : 0));
// entry
- data << (uint32) (item ? item->GetEntry() : 0);
+ data << uint32((item ? item->GetEntry() : 0));
for (uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
{
// unsure
- data << (uint32) (item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0);
+ data << uint32((item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0));
// unsure
- data << (uint32) (item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0);
+ data << uint32((item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0));
// unsure
- data << (uint32) (item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0);
+ data << uint32((item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0));
}
// can be negative
- data << (uint32) (item ? item->GetItemRandomPropertyId() : 0);
+ data << uint32((item ? item->GetItemRandomPropertyId() : 0));
// unk
- data << (uint32) (item ? item->GetItemSuffixFactor() : 0);
+ data << uint32((item ? item->GetItemSuffixFactor() : 0));
// stack count
- data << (uint32) (item ? item->GetCount() : 0);
+ data << uint32((item ? item->GetCount() : 0));
// charges
- data << (uint32) (item ? item->GetSpellCharges() : 0);
+ data << uint32((item ? item->GetSpellCharges() : 0));
// durability
- data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0);
+ data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0));
// durability
- data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0);
+ data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0));
// unknown wotlk
- data << (uint8) 0;
+ data << uint8(0);
}
- mails_count += 1;
+ mailsCount += 1;
}
- data.put<uint8>(0, mails_count); // set real send mails to client
+ data.put<uint8>(0, mailsCount); // set real send mails to client
SendPacket(&data);
// recalculate m_nextMailDelivereTime and unReadMails
@@ -711,7 +653,9 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
uint64 mailbox;
uint32 mailId;
- recv_data >> mailbox >> mailId;
+ recv_data >> mailbox;
+ recv_data >> mailId;
+ recv_data.read_skip<uint32>(); // mailTemplateId, non need, Mail store own 100% correct value anyway
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
@@ -719,27 +663,42 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
Player *pl = _player;
Mail* m = pl->GetMail(mailId);
- if(!m || !m->itemTextId || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
+ if (!m || !m->itemTextId && !m->mailTemplateId || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
{
pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
return;
}
+ uint32 itemTextId = m->itemTextId;
+
+ // in mail template case we need create new text id
+ if (!itemTextId)
+ {
+ MailTemplateEntry const* mailTemplateEntry = sMailTemplateStore.LookupEntry(m->mailTemplateId);
+ if (!mailTemplateEntry)
+ {
+ pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
+ return;
+ }
+
+ itemTextId = objmgr.CreateItemText(mailTemplateEntry->content[GetSessionDbcLocale()]);
+ }
+
Item *bodyItem = new Item; // This is not bag and then can be used new Item.
- if(!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl))
+ if (!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl))
{
delete bodyItem;
return;
}
- bodyItem->SetUInt32Value( ITEM_FIELD_ITEM_TEXT_ID , m->itemTextId );
- bodyItem->SetUInt32Value( ITEM_FIELD_CREATOR, m->sender);
+ bodyItem->SetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID , itemTextId);
+ bodyItem->SetUInt32Value(ITEM_FIELD_CREATOR, m->sender);
sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId);
ItemPosCountVec dest;
- uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, bodyItem, false );
- if( msg == EQUIP_ERR_OK )
+ uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, bodyItem, false);
+ if (msg == EQUIP_ERR_OK)
{
m->itemTextId = 0;
m->state = MAIL_STATE_CHANGED;
@@ -761,145 +720,278 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ )
{
WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8);
- if(!_player->m_mailsLoaded)
+ if (!_player->m_mailsLoaded)
_player->_LoadMail();
- if( _player->unReadMails > 0 )
+ if (_player->unReadMails > 0)
{
- data << (uint32) 0; // float
- data << (uint32) 0; // count
+ data << uint32(0); // float
+ data << uint32(0); // count
uint32 count = 0;
time_t now = time(NULL);
- for (PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr)
+ for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr)
{
Mail *m = (*itr);
// must be not checked yet
- if(m->checked & MAIL_CHECK_MASK_READ)
+ if (m->checked & MAIL_CHECK_MASK_READ)
continue;
// and already delivered
- if(now < m->deliver_time)
+ if (now < m->deliver_time)
continue;
- data << (uint64) m->sender; // sender guid
+ data << uint64(m->sender); // sender guid
switch(m->messageType)
{
case MAIL_AUCTION:
- data << (uint32) 2;
- data << (uint32) 2;
- data << (uint32) m->stationery;
+ data << uint32(2);
+ data << uint32(2);
+ data << uint32(m->stationery);
break;
default:
- data << (uint32) 0;
- data << (uint32) 0;
- data << (uint32) m->stationery;
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(m->stationery);
break;
}
- data << (uint32) 0xC6000000; // float unk, time or something
+ data << uint32(0xC6000000); // float unk, time or something
++count;
- if(count == 2) // do not display more than 2 mails
+ if (count == 2) // do not display more than 2 mails
break;
}
data.put<uint32>(4, count);
}
else
{
- data << (uint32) 0xC7A8C000;
- data << (uint32) 0x00000000;
+ data << uint32(0xC7A8C000);
+ data << uint32(0x00000000);
}
SendPacket(&data);
}
-void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 receiver_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay, uint16 mailTemplateId)
+MailSender::MailSender( Object* sender, MailStationery stationery ) : m_stationery(stationery)
+{
+ switch(sender->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ m_messageType = MAIL_CREATURE;
+ m_senderId = sender->GetEntry();
+ break;
+ case TYPEID_GAMEOBJECT:
+ m_messageType = MAIL_GAMEOBJECT;
+ m_senderId = sender->GetEntry();
+ break;
+ case TYPEID_ITEM:
+ m_messageType = MAIL_ITEM;
+ m_senderId = sender->GetEntry();
+ break;
+ case TYPEID_PLAYER:
+ m_messageType = MAIL_NORMAL;
+ m_senderId = sender->GetGUIDLow();
+ break;
+ default:
+ m_messageType = MAIL_NORMAL;
+ m_senderId = 0; // will show mail from not existed player
+ sLog.outError("MailSender::MailSender - Mail have unexpected sender typeid (%u)", sender->GetTypeId());
+ break;
+ }
+}
+
+MailSender::MailSender( AuctionEntry* sender )
+ : m_messageType(MAIL_AUCTION), m_senderId(sender->GetHouseId()), m_stationery(MAIL_STATIONERY_AUCTION)
+{
+}
+
+
+MailReceiver::MailReceiver( Player* receiver ) : m_receiver(receiver), m_receiver_lowguid(receiver->GetGUIDLow())
+{
+}
+
+MailReceiver::MailReceiver( Player* receiver,uint32 receiver_lowguid ) : m_receiver(receiver), m_receiver_lowguid(receiver_lowguid)
+{
+ ASSERT(!receiver || receiver->GetGUIDLow() == receiver_lowguid);
+}
+
+MailDraft& MailDraft::AddItem( Item* item )
+{
+ m_items[item->GetGUIDLow()] = item; return *this;
+}
+
+void MailDraft::prepareItems(Player* receiver)
{
- if (receiver_guidlow == auctionbot.GetAHBplayerGUID())
+ if (!m_mailTemplateId || !m_mailTemplateItemsNeed)
+ return;
+
+ m_mailTemplateItemsNeed = false;
+
+ Loot mailLoot;
+
+ // can be empty
+ mailLoot.FillLoot(m_mailTemplateId, LootTemplates_Mail, receiver, true, true);
+
+ uint32 max_slot = mailLoot.GetMaxSlotInLootFor(receiver);
+ for (uint32 i = 0; m_items.size() < MAX_MAIL_ITEMS && i < max_slot; ++i)
{
- if(messageType == MAIL_AUCTION && mi) // auction mail with items
+ if (LootItem* lootitem = mailLoot.LootItemInSlot(i,receiver))
{
- mi->deleteIncludedItems(true);
+ if (Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,receiver))
+ {
+ item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
+ AddItem(item);
+ }
}
+ }
+}
+
+void MailDraft::deleteIncludedItems( bool inDB /*= false*/ )
+{
+ for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
+ {
+ Item* item = mailItemIter->second;
+
+ if (inDB)
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow());
+
+ delete item;
+ }
+
+ m_items.clear();
+}
+
+void MailDraft::SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid )
+{
+ Player *receiver = objmgr.GetPlayer(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
+
+ uint32 rc_account = 0;
+ if (!receiver)
+ rc_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
+
+ if (!receiver && !rc_account) // sender not exist
+ {
+ deleteIncludedItems(true);
return;
}
+
+ // prepare mail and send in other case
+ bool needItemDelay = false;
+
+ if (!m_items.empty())
+ {
+ // if item send to character at another account, then apply item delivery delay
+ needItemDelay = sender_acc != rc_account;
+
+ // set owner to new receiver (to prevent delete item with sender char deleting)
+ CharacterDatabase.BeginTransaction();
+ for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
+ {
+ Item* item = mailItemIter->second;
+ item->SaveToDB(); // item not in inventory and can be save standalone
+ // owner in data will set at mail receive and item extracting
+ CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, item->GetGUIDLow());
+ }
+ CharacterDatabase.CommitTransaction();
+ }
+
+ // If theres is an item, there is a one hour delivery delay.
+ uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
+
+ // will delete item or place to receiver mail list
+ SendMailTo(MailReceiver(receiver,receiver_guid), MailSender(MAIL_NORMAL, sender_guid), MAIL_CHECK_MASK_RETURNED, deliver_delay);
+}
+
+void MailDraft::SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay)
+{
+ Player* pReceiver = receiver.GetPlayer(); // can be NULL
+
+ if (pReceiver)
+ prepareItems(pReceiver); // generate mail template items
+
uint32 mailId = objmgr.GenerateMailID();
+ if (receiver.GetPlayerGUIDLow() == auctionbot.GetAHBplayerGUID())
+ {
+ if (sender.GetMailMessageType() == MAIL_AUCTION) // auction mail with items
+ deleteIncludedItems(true);
+ return;
+ }
+
time_t deliver_time = time(NULL) + deliver_delay;
//expire time if COD 3 days, if no COD 30 days, if auction sale pending 1 hour
uint32 expire_delay;
- if(messageType == MAIL_AUCTION && !mi && !money) // auction mail without any items and money
+
+ // auction mail without any items and money
+ if (sender.GetMailMessageType() == MAIL_AUCTION && m_items.empty() && !m_money)
expire_delay = sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY);
+ // mail from battlemaster (rewardmarks) should last only one day
+ else if (sender.GetMailMessageType() == MAIL_CREATURE && sBattleGroundMgr.GetBattleMasterBG(sender.GetSenderId()) != BATTLEGROUND_TYPE_NONE)
+ expire_delay = DAY;
+ // default case: expire time if COD 3 days, if no COD 30 days
else
- expire_delay = (COD > 0) ? 3*DAY : 30*DAY;
+ expire_delay = (m_COD > 0) ? 3 * DAY : 30 * DAY;
time_t expire_time = deliver_time + expire_delay;
- if(mailTemplateId && !sMailTemplateStore.LookupEntry(mailTemplateId))
+ // Add to DB
+ std::string safe_subject = GetSubject();
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.escape_string(safe_subject);
+ CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" UI64FMTD "','" UI64FMTD "', '%u', '%u', '%d')",
+ mailId, sender.GetMailMessageType(), sender.GetStationery(), GetMailTemplateId(), sender.GetSenderId(), receiver.GetPlayerGUIDLow(), safe_subject.c_str(), GetBodyId(), (m_items.empty() ? 0 : 1), (uint64)expire_time, (uint64)deliver_time, m_money, m_COD, checked);
+
+ for (MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
{
- sLog.outError( "WorldSession::SendMailTo - Mail have not existed MailTemplateId (%u), remove at send", mailTemplateId);
- mailTemplateId = 0;
+ Item* item = mailItemIter->second;
+ CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, item->GetGUIDLow(), item->GetEntry(), receiver.GetPlayerGUIDLow());
}
+ CharacterDatabase.CommitTransaction();
- if(receiver)
+ // For online receiver update in game mail status and data
+ if (pReceiver)
{
- receiver->AddNewMailDeliverTime(deliver_time);
+ pReceiver->AddNewMailDeliverTime(deliver_time);
- if ( receiver->IsMailsLoaded() )
+ if (pReceiver->IsMailsLoaded())
{
Mail * m = new Mail;
m->messageID = mailId;
- m->messageType = messageType;
- m->stationery = stationery;
- m->mailTemplateId = mailTemplateId;
- m->sender = sender_guidlow_or_entry;
- m->receiver = receiver->GetGUIDLow();
- m->subject = subject;
- m->itemTextId = itemTextId;
+ m->mailTemplateId = GetMailTemplateId();
+ m->subject = GetSubject();
+ m->itemTextId = GetBodyId();
+ m->money = GetMoney();
+ m->COD = GetCOD();
- if(mi)
- m->AddAllItems(*mi);
+ for (MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
+ {
+ Item* item = mailItemIter->second;
+ m->AddItem(item->GetGUIDLow(), item->GetEntry());
+ }
+ m->messageType = sender.GetMailMessageType();
+ m->stationery = sender.GetStationery();
+ m->sender = sender.GetSenderId();
+ m->receiver = receiver.GetPlayerGUIDLow();
m->expire_time = expire_time;
m->deliver_time = deliver_time;
- m->money = money;
- m->COD = COD;
m->checked = checked;
m->state = MAIL_STATE_UNCHANGED;
- receiver->AddMail(m); //to insert new mail to beginning of maillist
+ pReceiver->AddMail(m); // to insert new mail to beginning of maillist
- if(mi)
+ if (!m_items.empty())
{
- for (MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
- {
- MailItem& mailItem = mailItemIter->second;
- if(mailItem.item)
- receiver->AddMItem(mailItem.item);
- }
+ for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
+ pReceiver->AddMItem(mailItemIter->second);
}
}
- else if(mi)
- mi->deleteIncludedItems();
- }
- else if(mi)
- mi->deleteIncludedItems();
-
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.escape_string(subject);
- CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) "
- "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" UI64FMTD "','" UI64FMTD "', '%u', '%u', '%d')",
- mailId, messageType, stationery, mailTemplateId, sender_guidlow_or_entry, receiver_guidlow, subject.c_str(), itemTextId, (mi && !mi->empty() ? 1 : 0), (uint64)expire_time, (uint64)deliver_time, money, COD, checked);
-
- if(mi)
- {
- for (MailItemMap::const_iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
- {
- MailItem const& mailItem = mailItemIter->second;
- CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, mailItem.item_guidlow, mailItem.item_template,receiver_guidlow);
- }
+ else if (!m_items.empty())
+ deleteIncludedItems();
}
- CharacterDatabase.CommitTransaction();
+ else if (!m_items.empty())
+ deleteIncludedItems();
}
-
diff --git a/src/game/Mail.h b/src/game/Mail.h
index 8107f879e6a..64ad72757b6 100644
--- a/src/game/Mail.h
+++ b/src/game/Mail.h
@@ -23,20 +23,14 @@
#include "Common.h"
#include <map>
+struct AuctionEntry;
class Item;
+class Object;
+class Player;
#define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889
#define MAX_MAIL_ITEMS 12
-enum MailCheckMask
-{
- MAIL_CHECK_MASK_NONE = 0,
- MAIL_CHECK_MASK_READ = 1,
- MAIL_CHECK_MASK_AUCTION = 4,
- MAIL_CHECK_MASK_COD_PAYMENT = 8,
- MAIL_CHECK_MASK_RETURNED = 16
-};
-
enum MailMessageType
{
MAIL_NORMAL = 0,
@@ -46,6 +40,26 @@ enum MailMessageType
MAIL_ITEM = 5, // client send CMSG_ITEM_QUERY on this mailmessagetype
};
+enum MailCheckMask
+{
+ MAIL_CHECK_MASK_NONE = 0x00,
+ MAIL_CHECK_MASK_READ = 0x01,
+ MAIL_CHECK_MASK_AUCTION = 0x04,
+ MAIL_CHECK_MASK_COD_PAYMENT = 0x08,
+ MAIL_CHECK_MASK_RETURNED = 0x10
+};
+
+// gathered from Stationery.dbc
+enum MailStationery
+{
+ MAIL_STATIONERY_UNKNOWN = 1,
+ MAIL_STATIONERY_NORMAL = 41,
+ MAIL_STATIONERY_GM = 61,
+ MAIL_STATIONERY_AUCTION = 62,
+ MAIL_STATIONERY_VAL = 64,
+ MAIL_STATIONERY_CHR = 65,
+};
+
enum MailState
{
MAIL_STATE_UNCHANGED = 1,
@@ -64,76 +78,81 @@ enum MailAuctionAnswers
AUCTION_SALE_PENDING = 6
};
-// gathered from Stationery.dbc
-enum MailStationery
+class MailSender
{
- MAIL_STATIONERY_UNKNOWN = 0x01,
- MAIL_STATIONERY_NORMAL = 0x29,
- MAIL_STATIONERY_GM = 0x3D,
- MAIL_STATIONERY_AUCTION = 0x3E,
- MAIL_STATIONERY_VAL = 0x40,
- MAIL_STATIONERY_CHR = 0x41
+ public: // Constructors
+ MailSender(MailMessageType messageType, uint32 sender_guidlow_or_entry, MailStationery stationery = MAIL_STATIONERY_NORMAL)
+ : m_messageType(messageType), m_senderId(sender_guidlow_or_entry), m_stationery(stationery)
+ {
+ }
+ MailSender(Object* sender, MailStationery stationery = MAIL_STATIONERY_NORMAL);
+ MailSender(AuctionEntry* sender);
+ public: // Accessors
+ MailMessageType GetMailMessageType() const { return m_messageType; }
+ uint32 GetSenderId() const { return m_senderId; }
+ MailStationery GetStationery() const { return m_stationery; }
+ private:
+ MailMessageType m_messageType;
+ uint32 m_senderId; // player low guid or other object entry
+ MailStationery m_stationery;
};
-struct MailItemInfo
+class MailReceiver
{
- uint32 item_guid;
- uint32 item_template;
+ public: // Constructors
+ explicit MailReceiver(uint32 receiver_lowguid) : m_receiver(NULL), m_receiver_lowguid(receiver_lowguid) {}
+ MailReceiver(Player* receiver);
+ MailReceiver(Player* receiver,uint32 receiver_lowguid);
+ public: // Accessors
+ Player* GetPlayer() const { return m_receiver; }
+ uint32 GetPlayerGUIDLow() const { return m_receiver_lowguid; }
+ private:
+ Player* m_receiver;
+ uint32 m_receiver_lowguid;
};
-struct MailItem
+class MailDraft
{
- MailItem() : item_slot(0), item_guidlow(0), item_template(0), item(NULL) {}
+ typedef std::map<uint32, Item*> MailItemMap;
+
+ public: // Constructors
+ explicit MailDraft(uint16 mailTemplateId, bool need_items = true)
+ : m_mailTemplateId(mailTemplateId), m_mailTemplateItemsNeed(need_items), m_bodyId(0), m_money(0), m_COD(0)
+ {}
+ MailDraft(std::string subject, uint32 itemTextId = 0)
+ : m_mailTemplateId(0), m_mailTemplateItemsNeed(false), m_subject(subject), m_bodyId(itemTextId), m_money(0), m_COD(0) {}
+ public: // Accessors
+ uint16 GetMailTemplateId() const { return m_mailTemplateId; }
+ std::string const& GetSubject() const { return m_subject; }
+ uint32 GetBodyId() const { return m_bodyId; }
+ uint32 GetMoney() const { return m_money; }
+ uint32 GetCOD() const { return m_COD; }
+ public: // modifiers
+ MailDraft& AddItem(Item* item);
+ MailDraft& AddMoney(uint32 money) { m_money = money; return *this; }
+ MailDraft& AddCOD(uint32 COD) { m_COD = COD; return *this; }
+ public: // finishers
+ void SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid);
+ void SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0);
+ private:
+ void deleteIncludedItems(bool inDB = false);
+ void prepareItems(Player* receiver); // called from SendMailTo for generate mailTemplateBase items
- uint8 item_slot; // slot in mail
- uint32 item_guidlow; // item guid (low part)
- uint32 item_template; // item entry
- Item *item; // item pointer
+ uint16 m_mailTemplateId;
+ bool m_mailTemplateItemsNeed;
+ std::string m_subject;
+ uint32 m_bodyId;
- void deleteItem(bool inDB = false);
-};
+ MailItemMap m_items; // Keep the items in a map to avoid duplicate guids (which can happen), store only low part of guid
-typedef std::map<uint32, MailItem> MailItemMap;
+ uint32 m_money;
+ uint32 m_COD;
+};
-class MailItemsInfo
+struct MailItemInfo
{
- public:
- MailItemMap::const_iterator begin() const { return i_MailItemMap.begin(); }
- MailItemMap::const_iterator end() const { return i_MailItemMap.end(); }
- MailItemMap::iterator begin() { return i_MailItemMap.begin(); }
- MailItemMap::iterator end() { return i_MailItemMap.end(); }
-
- void AddItem(uint32 guidlow, uint32 _template, Item *item, uint8 slot = 0)
- {
- MailItem mailItem;
- mailItem.item_slot = slot;
- mailItem.item_guidlow = guidlow;
- mailItem.item_template = _template;
- mailItem.item = item;
- i_MailItemMap[guidlow] = mailItem;
- }
-
- void AddItem(uint32 guidlow, uint8 slot = 0)
- {
- MailItem mailItem;
- mailItem.item_guidlow = guidlow;
- mailItem.item_slot = slot;
- i_MailItemMap[guidlow] = mailItem;
- }
-
- uint8 size() const { return i_MailItemMap.size(); }
- bool empty() const { return i_MailItemMap.empty(); }
-
- void deleteIncludedItems(bool inDB = false)
- {
- for (MailItemMap::iterator mailItemIter = begin(); mailItemIter != end(); ++mailItemIter)
- {
- MailItem& mailItem = mailItemIter->second;
- mailItem.deleteItem(inDB);
- }
- }
- private:
- MailItemMap i_MailItemMap; // Keep the items in a map to avoid duplicate guids (which can happen), store only low part of guid
+ uint32 item_guid;
+ uint32 item_template;
};
struct Mail
@@ -163,15 +182,6 @@ struct Mail
items.push_back(mii);
}
- void AddAllItems(MailItemsInfo& pMailItemsInfo)
- {
- for (MailItemMap::iterator mailItemIter = pMailItemsInfo.begin(); mailItemIter != pMailItemsInfo.end(); ++mailItemIter)
- {
- MailItem& mailItem = mailItemIter->second;
- AddItem(mailItem.item_guidlow, mailItem.item_template);
- }
- }
-
bool RemoveItem(uint32 item_guid)
{
for (std::vector<MailItemInfo>::iterator itr = items.begin(); itr != items.end(); ++itr)
@@ -187,5 +197,5 @@ struct Mail
bool HasItems() const { return !items.empty(); }
};
-#endif
+#endif
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp
index 830df309021..1d9f2d64878 100644
--- a/src/game/ObjectMgr.cpp
+++ b/src/game/ObjectMgr.cpp
@@ -3537,6 +3537,9 @@ void ObjectMgr::LoadQuests()
delete result;
+
+ std::map<uint32,uint32> usedMailTemplates;
+
// Post processing
for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
{
@@ -4030,21 +4033,31 @@ void ObjectMgr::LoadQuests()
}
}
- if(qinfo->RewMailTemplateId)
+ if (qinfo->RewMailTemplateId)
{
- if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
+ if (!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
{
sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
qinfo->RewMailTemplateId = 0; // no mail will send to player
qinfo->RewMailDelaySecs = 0; // no mail will send to player
}
+ else if (usedMailTemplates.find(qinfo->RewMailTemplateId) != usedMailTemplates.end())
+ {
+ std::map<uint32,uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewMailTemplateId);
+ sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
+ qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId,used_mt_itr->second);
+ qinfo->RewMailTemplateId = 0; // no mail will send to player
+ qinfo->RewMailDelaySecs = 0; // no mail will send to player
+ }
+ else
+ usedMailTemplates[qinfo->RewMailTemplateId] = qinfo->GetQuestId();
}
- if(qinfo->NextQuestInChain)
+ if (qinfo->NextQuestInChain)
{
QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
- if(qNextItr == mQuestTemplates.end())
+ if (qNextItr == mQuestTemplates.end())
{
sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
@@ -4055,7 +4068,7 @@ void ObjectMgr::LoadQuests()
}
// fill additional data stores
- if(qinfo->PrevQuestId)
+ if (qinfo->PrevQuestId)
{
if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
{
@@ -4067,7 +4080,7 @@ void ObjectMgr::LoadQuests()
}
}
- if(qinfo->NextQuestId)
+ if (qinfo->NextQuestId)
{
QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId()));
if (qNextItr == mQuestTemplates.end())
@@ -4081,9 +4094,9 @@ void ObjectMgr::LoadQuests()
}
}
- if(qinfo->ExclusiveGroup)
+ if (qinfo->ExclusiveGroup)
mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
- if(qinfo->LimitTime)
+ if (qinfo->LimitTime)
qinfo->SetFlag(QUEST_TRINITY_FLAGS_TIMED);
}
@@ -7985,6 +7998,72 @@ bool ObjectMgr::DeleteGameTele(const std::string& name)
return false;
}
+void ObjectMgr::LoadMailLevelRewards()
+{
+ m_mailLevelRewardMap.clear(); // for reload case
+
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `mail_level_reward`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint8 level = fields[0].GetUInt8();
+ uint32 raceMask = fields[1].GetUInt32();
+ uint32 mailTemplateId = fields[2].GetUInt32();
+ uint32 senderEntry = fields[3].GetUInt32();
+
+ if(level > MAX_LEVEL)
+ {
+ sLog.outErrorDb("Table `mail_level_reward` have data for level %u that more supported by client (%u), ignoring.",level,MAX_LEVEL);
+ continue;
+ }
+
+ if(!(raceMask & RACEMASK_ALL_PLAYABLE))
+ {
+ sLog.outErrorDb("Table `mail_level_reward` have raceMask (%u) for level %u that not include any player races, ignoring.",raceMask,level);
+ continue;
+ }
+
+ if(!sMailTemplateStore.LookupEntry(mailTemplateId))
+ {
+ sLog.outErrorDb("Table `mail_level_reward` have invalid mailTemplateId (%u) for level %u that invalid not include any player races, ignoring.",mailTemplateId,level);
+ continue;
+ }
+
+ if(!GetCreatureTemplateStore(senderEntry))
+ {
+ sLog.outErrorDb("Table `mail_level_reward` have not existed sender creature entry (%u) for level %u that invalid not include any player races, ignoring.",senderEntry,level);
+ continue;
+ }
+
+ m_mailLevelRewardMap[level].push_back(MailLevelReward(raceMask,mailTemplateId,senderEntry));
+
+ ++count;
+ }
+ while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level dependent mail rewards,", count );
+}
+
void ObjectMgr::LoadTrainerSpell()
{
// For reload case
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h
index 0ca6a5f2422..1c5367387fc 100644
--- a/src/game/ObjectMgr.h
+++ b/src/game/ObjectMgr.h
@@ -180,6 +180,19 @@ struct PetLevelInfo
uint16 armor;
};
+struct MailLevelReward
+{
+ MailLevelReward() : raceMask(0), mailTemplateId(0), senderEntry(0) {}
+ MailLevelReward(uint32 _raceMask, uint32 _mailTemplateId, uint32 _senderEntry) : raceMask(_raceMask), mailTemplateId(_mailTemplateId), senderEntry(_senderEntry) {}
+
+ uint32 raceMask;
+ uint32 mailTemplateId;
+ uint32 senderEntry;
+};
+
+typedef std::list<MailLevelReward> MailLevelRewardList;
+typedef UNORDERED_MAP<uint8,MailLevelRewardList> MailLevelRewardMap;
+
struct ReputationOnKillEntry
{
uint32 repfaction1;
@@ -570,6 +583,7 @@ class ObjectMgr
void LoadNpcOptionLocales();
void LoadPointOfInterestLocales();
void LoadInstanceTemplate();
+ void LoadMailLevelRewards();
void LoadGossipText();
@@ -645,6 +659,19 @@ class ObjectMgr
typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
ExclusiveQuestGroups mExclusiveQuestGroups;
+ MailLevelReward const* GetMailLevelReward(uint32 level,uint32 raceMask)
+ {
+ MailLevelRewardMap::const_iterator map_itr = m_mailLevelRewardMap.find(level);
+ if (map_itr == m_mailLevelRewardMap.end())
+ return NULL;
+
+ for(MailLevelRewardList::const_iterator set_itr = map_itr->second.begin(); set_itr != map_itr->second.end(); ++set_itr)
+ if (set_itr->raceMask & raceMask)
+ return &*set_itr;
+
+ return NULL;
+ }
+
WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
{
WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
@@ -956,6 +983,8 @@ class ObjectMgr
void ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr);
void LoadQuestRelationsHelper(QuestRelations& map,char const* table);
+ MailLevelRewardMap m_mailLevelRewardMap;
+
typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
// PetLevelInfoMap[creature_id][level]
PetLevelInfoMap petInfo; // [creature_id][level]
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index e5613cbb703..d0b1a25f192 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -2566,6 +2566,9 @@ void Player::GiveLevel(uint8 level)
if (Pet* pet = GetPet())
pet->SynchronizeLevelWithOwner();
+ if (MailLevelReward const* mailReward = objmgr.GetMailLevelReward(level,getRaceMask()))
+ MailDraft(mailReward->mailTemplateId).SendMailTo(this,MailSender(MAIL_CREATURE,mailReward->senderEntry));
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
}
@@ -4131,8 +4134,8 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
// remove signs from petitions (also remove petitions if owner);
RemovePetitionsAndSigns(playerguid, 10);
- // return back all mails with COD and Item 0 1 2 3 4 5 6
- QueryResult *resultMail = CharacterDatabase.PQuery("SELECT id,mailTemplateId,sender,subject,itemTextId,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
+ // return back all mails with COD and Item 0 1 2 3 4 5 6 7
+ QueryResult *resultMail = CharacterDatabase.PQuery("SELECT id,messageType,mailTemplateId,sender,subject,itemTextId,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
if(resultMail)
{
do
@@ -4140,18 +4143,30 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
Field *fields = resultMail->Fetch();
uint32 mail_id = fields[0].GetUInt32();
- uint16 mailTemplateId= fields[1].GetUInt16();
- uint32 sender = fields[2].GetUInt32();
- std::string subject = fields[3].GetCppString();
- uint32 itemTextId = fields[4].GetUInt32();
- uint32 money = fields[5].GetUInt32();
- bool has_items = fields[6].GetBool();
+ uint16 mailType = fields[1].GetUInt16();
+ uint16 mailTemplateId= fields[2].GetUInt16();
+ uint32 sender = fields[3].GetUInt32();
+ std::string subject = fields[4].GetCppString();
+ uint32 itemTextId = fields[5].GetUInt32();
+ uint32 money = fields[6].GetUInt32();
+ bool has_items = fields[7].GetBool();
//we can return mail now
//so firstly delete the old one
CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
- MailItemsInfo mi;
+ // mail not from player
+ if (mailType != MAIL_NORMAL)
+ {
+ if(has_items)
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
+ continue;
+ }
+
+ MailDraft draft(subject, itemTextId);
+ if (mailTemplateId)
+ draft = MailDraft(mailTemplateId,false); // itesm already included
+
if(has_items)
{
// data needs to be at first place for Item::LoadFromDB
@@ -4180,7 +4195,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
continue;
}
- mi.AddItem(item_guidlow, item_template, pItem);
+ draft.AddItem(pItem);
}
while (resultItems->NextRow());
@@ -4192,7 +4207,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
uint32 pl_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
- WorldSession::SendReturnToSender(MAIL_NORMAL, pl_account, guid, sender, subject, itemTextId, &mi, money, mailTemplateId);
+ draft.AddMoney(money).SendReturnToSender(pl_account, guid, sender);
}
while (resultMail->NextRow());
@@ -7853,7 +7868,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
{
sLog.outDebug(" if(lootid)");
loot->clear();
- loot->FillLoot(lootid, LootTemplates_Gameobject, this, false, go->GetLootMode());
+ loot->FillLoot(lootid, LootTemplates_Gameobject, this, false, false, go->GetLootMode());
}
if (loot_type == LOOT_FISHING)
@@ -7982,7 +7997,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
loot->clear();
if (uint32 lootid = creature->GetCreatureInfo()->lootid)
- loot->FillLoot(lootid, LootTemplates_Creature, recipient, false, creature->GetLootMode());
+ loot->FillLoot(lootid, LootTemplates_Creature, recipient, false, false, creature->GetLootMode());
loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
@@ -13267,56 +13282,8 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
}
// Send reward mail
- if (pQuest->GetRewMailTemplateId())
- {
- MailMessageType mailType;
- uint32 senderGuidOrEntry;
- switch(questGiver->GetTypeId())
- {
- case TYPEID_UNIT:
- mailType = MAIL_CREATURE;
- senderGuidOrEntry = questGiver->GetEntry();
- break;
- case TYPEID_GAMEOBJECT:
- mailType = MAIL_GAMEOBJECT;
- senderGuidOrEntry = questGiver->GetEntry();
- break;
- case TYPEID_ITEM:
- mailType = MAIL_ITEM;
- senderGuidOrEntry = questGiver->GetEntry();
- break;
- case TYPEID_PLAYER:
- mailType = MAIL_NORMAL;
- senderGuidOrEntry = questGiver->GetGUIDLow();
- break;
- default:
- mailType = MAIL_NORMAL;
- senderGuidOrEntry = GetGUIDLow();
- break;
- }
-
- Loot questMailLoot;
-
- questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this,true);
-
- // fill mail
- MailItemsInfo mi; // item list preparing
-
- uint32 max_slot = questMailLoot.GetMaxSlotInLootFor(this);
- for (uint32 i = 0; mi.size() < MAX_MAIL_ITEMS && i < max_slot; ++i)
- {
- if (LootItem* lootitem = questMailLoot.LootItemInSlot(i,this))
- {
- if (Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this))
- {
- item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
- }
- }
- }
-
- WorldSession::SendMailTo(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId());
- }
+ if (uint32 mail_template_id = pQuest->GetRewMailTemplateId())
+ MailDraft(mail_template_id).SendMailTo(this, questGiver, MAIL_CHECK_MASK_NONE, pQuest->GetRewMailDelaySecs());
if (pQuest->IsDaily())
{
@@ -15641,20 +15608,20 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
// send by mail problematic items
while (!problematicItems.empty())
{
+ std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
+
// fill mail
- MailItemsInfo mi; // item list preparing
+ MailDraft draft(subject);
for (uint8 i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
{
Item* item = problematicItems.front();
problematicItems.pop_front();
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ draft.AddItem(item);
}
- std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM);
-
- WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ draft.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM));
}
}
//if(isAlive())
@@ -20026,8 +19993,6 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
}
else
{
- MailItemsInfo mi;
- mi.AddItem(offItem->GetGUIDLow(), offItem->GetEntry(), offItem);
MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
CharacterDatabase.BeginTransaction();
offItem->DeleteFromInventoryDB(); // deletes item from character's inventory
@@ -20035,7 +20000,7 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
CharacterDatabase.CommitTransaction();
std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
- WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ MailDraft(subject).AddItem(offItem).SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM));
}
}
diff --git a/src/game/Player.h b/src/game/Player.h
index 162e2e29b00..94cb99bb883 100644
--- a/src/game/Player.h
+++ b/src/game/Player.h
@@ -1428,8 +1428,8 @@ class MANGOS_DLL_SPEC Player : public Unit
uint32 GetMailSize() { return m_mail.size();};
Mail* GetMail(uint32 id);
- PlayerMails::iterator GetmailBegin() { return m_mail.begin();};
- PlayerMails::iterator GetmailEnd() { return m_mail.end();};
+ PlayerMails::iterator GetMailBegin() { return m_mail.begin();};
+ PlayerMails::iterator GetMailEnd() { return m_mail.end();};
/*********************************************************/
/*** MAILED ITEMS SYSTEM ***/
diff --git a/src/game/World.cpp b/src/game/World.cpp
index b343798dbf7..43f2c40026d 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -1477,6 +1477,9 @@ void World::SetInitialWorldSettings()
sLog.outString("Loading Player Corpses...");
objmgr.LoadCorpses();
+ sLog.outString( "Loading Player level dependent mail rewards..." );
+ objmgr.LoadMailLevelRewards();
+
sLog.outString("Loading Disabled Spells...");
objmgr.LoadSpellDisabledEntrys();
diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h
index b26f5ba4933..021a1a49d2b 100644
--- a/src/game/WorldSession.h
+++ b/src/game/WorldSession.h
@@ -28,7 +28,6 @@
#include "Common.h"
#include "SharedDefines.h"
-class MailItemsInfo;
struct ItemPrototype;
struct AuctionEntry;
struct DeclinedName;
@@ -42,7 +41,6 @@ class Unit;
class GameObject;
class WorldPacket;
class WorldSocket;
-class WorldSession;
class QueryResult;
class LoginQueryHolder;
class CharacterHandler;
@@ -223,12 +221,8 @@ class TRINITY_DLL_SPEC WorldSession
m_TutorialsChanged = true;
}
}
-
- //mail
- //used with item_page table
+ //used with item_page table
bool SendItemInfo( uint32 itemid, WorldPacket data );
- static void SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, const std::string& subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint16 mailTemplateId = 0);
- static void SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 received_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay = 0, uint16 mailTemplateId = 0);
//auction
void SendAuctionHello( uint64 guid, Creature * unit );