diff options
author | win32 <none@none> | 2009-12-16 18:58:36 +0200 |
---|---|---|
committer | win32 <none@none> | 2009-12-16 18:58:36 +0200 |
commit | bf2c3fd8cf32578324162a8bef2ac6e48e7e0009 (patch) | |
tree | ccb8f71a159aba2799cc009352a8626d95b6fc6c | |
parent | dc53b4a9def06d012d791304aaba1c748a1f997a (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
-rw-r--r-- | sql/updates/6521_world_command.sql | 1 | ||||
-rw-r--r-- | sql/updates/6521_world_mail_level_reward.sql | 15 | ||||
-rw-r--r-- | sql/updates/6521_world_quest_mail_loot_template.sql | 4 | ||||
-rw-r--r-- | sql/world.sql | 55 | ||||
-rw-r--r-- | src/game/AchievementMgr.cpp | 23 | ||||
-rw-r--r-- | src/game/AuctionHouseHandler.cpp | 15 | ||||
-rw-r--r-- | src/game/AuctionHouseMgr.cpp | 21 | ||||
-rw-r--r-- | src/game/BattleGround.cpp | 8 | ||||
-rw-r--r-- | src/game/Chat.cpp | 3 | ||||
-rw-r--r-- | src/game/Chat.h | 3 | ||||
-rw-r--r-- | src/game/DBCStructure.h | 2 | ||||
-rw-r--r-- | src/game/DBCfmt.h | 2 | ||||
-rw-r--r-- | src/game/GameObject.cpp | 6 | ||||
-rw-r--r-- | src/game/Level1.cpp | 7 | ||||
-rw-r--r-- | src/game/Level3.cpp | 37 | ||||
-rw-r--r-- | src/game/LootMgr.cpp | 38 | ||||
-rw-r--r-- | src/game/LootMgr.h | 8 | ||||
-rw-r--r-- | src/game/Mail.cpp | 670 | ||||
-rw-r--r-- | src/game/Mail.h | 164 | ||||
-rw-r--r-- | src/game/ObjectMgr.cpp | 95 | ||||
-rw-r--r-- | src/game/ObjectMgr.h | 29 | ||||
-rw-r--r-- | src/game/Player.cpp | 107 | ||||
-rw-r--r-- | src/game/Player.h | 4 | ||||
-rw-r--r-- | src/game/World.cpp | 3 | ||||
-rw-r--r-- | src/game/WorldSession.h | 8 |
25 files changed, 768 insertions, 560 deletions
diff --git a/sql/updates/6521_world_command.sql b/sql/updates/6521_world_command.sql new file mode 100644 index 00000000000..11bb1c404ce --- /dev/null +++ b/sql/updates/6521_world_command.sql @@ -0,0 +1 @@ +UPDATE `command` SET `name`='reload mail_loot_template' WHERE `name`='reload quest_mail_loot_template';
\ No newline at end of file diff --git a/sql/updates/6521_world_mail_level_reward.sql b/sql/updates/6521_world_mail_level_reward.sql new file mode 100644 index 00000000000..656bb83c69c --- /dev/null +++ b/sql/updates/6521_world_mail_level_reward.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS `mail_level_reward`; +CREATE TABLE `mail_level_reward` ( + `level` tinyint(3) unsigned NOT NULL default '0', + `raceMask` mediumint(8) unsigned NOT NULL default '0', + `mailTemplateId` mediumint(8) unsigned NOT NULL default '0', + `senderEntry` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`level`,`raceMask`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Mail System'; + +INSERT INTO `mail_level_reward` VALUES +(20, 1, 224, 4732),(20, 8, 225, 4753), +(20, 4, 226, 4772),(20, 1024, 227, 20914), +(20, 64, 228, 7954),(20, 32, 229, 3690), +(20, 128, 230, 7953),(20, 2, 231, 4752), +(20, 512, 232, 16280),(20, 16, 233, 4773);
\ No newline at end of file diff --git a/sql/updates/6521_world_quest_mail_loot_template.sql b/sql/updates/6521_world_quest_mail_loot_template.sql new file mode 100644 index 00000000000..08ebe03eca4 --- /dev/null +++ b/sql/updates/6521_world_quest_mail_loot_template.sql @@ -0,0 +1,4 @@ +RENAME TABLE quest_mail_loot_template TO mail_loot_template; + +UPDATE mail_loot_template, quest_template + SET mail_loot_template.entry = quest_template.RewMailTemplateId WHERE mail_loot_template.entry = quest_template.entry;
\ No newline at end of file diff --git a/sql/world.sql b/sql/world.sql index 3907d139b01..d8a20c724b0 100644 --- a/sql/world.sql +++ b/sql/world.sql @@ -1814,6 +1814,40 @@ CREATE TABLE `locales_quest` ( SET character_set_client = @saved_cs_client; -- +-- Table structure for table `mail_level_reward` +-- + +DROP TABLE IF EXISTS `mail_level_reward`; +CREATE TABLE `mail_level_reward` ( + `level` tinyint(3) unsigned NOT NULL default '0', + `raceMask` mediumint(8) unsigned NOT NULL default '0', + `mailTemplateId` mediumint(8) unsigned NOT NULL default '0', + `senderEntry` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`level`,`raceMask`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Mail System'; + +-- +-- Table structure for table `mail_loot_template` +-- + +DROP TABLE IF EXISTS `mail_loot_template`; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +CREATE TABLE `mail_loot_template` ( + `entry` mediumint(8) unsigned NOT NULL default '0', + `item` mediumint(8) unsigned NOT NULL default '0', + `ChanceOrQuestChance` float NOT NULL default '100', + `groupid` tinyint(3) unsigned NOT NULL default '0', + `mincountOrRef` mediumint(9) NOT NULL default '1', + `maxcount` tinyint(3) unsigned NOT NULL default '1', + `lootcondition` tinyint(3) unsigned NOT NULL default '0', + `condition_value1` mediumint(8) unsigned NOT NULL default '0', + `condition_value2` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`entry`,`item`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; +SET character_set_client = @saved_cs_client; + +-- -- Table structure for table `milling_loot_template` -- @@ -2380,27 +2414,6 @@ CREATE TABLE `quest_end_scripts` ( SET character_set_client = @saved_cs_client; -- --- Table structure for table `quest_mail_loot_template` --- - -DROP TABLE IF EXISTS `quest_mail_loot_template`; -SET @saved_cs_client = @@character_set_client; -SET character_set_client = utf8; -CREATE TABLE `quest_mail_loot_template` ( - `entry` mediumint(8) unsigned NOT NULL default '0', - `item` mediumint(8) unsigned NOT NULL default '0', - `ChanceOrQuestChance` float NOT NULL default '100', - `groupid` tinyint(3) unsigned NOT NULL default '0', - `mincountOrRef` mediumint(9) NOT NULL default '1', - `maxcount` tinyint(3) unsigned NOT NULL default '1', - `lootcondition` tinyint(3) unsigned NOT NULL default '0', - `condition_value1` mediumint(8) unsigned NOT NULL default '0', - `condition_value2` mediumint(8) unsigned NOT NULL default '0', - PRIMARY KEY (`entry`,`item`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; -SET character_set_client = @saved_cs_client; - --- -- Table structure for table `quest_start_scripts` -- 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 ); |