diff --git a/sql/mangos.sql b/sql/mangos.sql index 37ba033a7..96c8df0b6 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_8731_01_mangos_creature_template` bit(1) default NULL + `required_8770_01_mangos_quest_template` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -2711,6 +2711,55 @@ LOCK TABLES `locales_quest` WRITE; /*!40000 ALTER TABLE `locales_quest` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `mail_level_reward` +-- + +DROP TABLE IF EXISTS `mail_level_reward`; +CREATE TABLE `mail_level_reward` ( + `level` mediumint(8) 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'; + +-- +-- Dumping data for table `mail_level_reward` +-- + +LOCK TABLES `mail_level_reward` WRITE; +/*!40000 ALTER TABLE `mail_level_reward` DISABLE KEYS */; +/*!40000 ALTER TABLE `mail_level_reward` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `mail_loot_template` +-- + +DROP TABLE IF EXISTS `mail_loot_template`; +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'; + +-- +-- Dumping data for table `mail_loot_template` +-- + +LOCK TABLES `mail_loot_template` WRITE; +/*!40000 ALTER TABLE `mail_loot_template` DISABLE KEYS */; +/*!40000 ALTER TABLE `mail_loot_template` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `mangos_string` -- @@ -13561,33 +13610,6 @@ LOCK TABLES `quest_start_scripts` WRITE; /*!40000 ALTER TABLE `quest_start_scripts` ENABLE KEYS */; UNLOCK TABLES; --- --- Table structure for table `quest_mail_loot_template` --- - -DROP TABLE IF EXISTS `quest_mail_loot_template`; -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'; - --- --- Dumping data for table `quest_mail_loot_template` --- - -LOCK TABLES `quest_mail_loot_template` WRITE; -/*!40000 ALTER TABLE `quest_mail_loot_template` DISABLE KEYS */; -/*!40000 ALTER TABLE `quest_mail_loot_template` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `quest_template` -- @@ -13599,7 +13621,7 @@ CREATE TABLE `quest_template` ( `ZoneOrSort` smallint(6) NOT NULL default '0', `SkillOrClass` smallint(6) NOT NULL default '0', `MinLevel` tinyint(3) unsigned NOT NULL default '0', - `QuestLevel` tinyint(3) unsigned NOT NULL default '0', + `QuestLevel` smallint(6) NOT NULL default '0', `Type` smallint(5) unsigned NOT NULL default '0', `RequiredRaces` smallint(5) unsigned NOT NULL default '0', `RequiredSkillValue` smallint(5) unsigned NOT NULL default '0', diff --git a/sql/updates/8749_01_mangos_mail_loot_template.sql b/sql/updates/8749_01_mangos_mail_loot_template.sql new file mode 100644 index 000000000..77d5e7edf --- /dev/null +++ b/sql/updates/8749_01_mangos_mail_loot_template.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_8731_01_mangos_creature_template required_8749_01_mangos_mail_loot_template bit; + +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; diff --git a/sql/updates/8769_01_mangos_mail_level_reward.sql b/sql/updates/8769_01_mangos_mail_level_reward.sql new file mode 100644 index 000000000..30e5e2d50 --- /dev/null +++ b/sql/updates/8769_01_mangos_mail_level_reward.sql @@ -0,0 +1,10 @@ +ALTER TABLE db_version CHANGE COLUMN required_8749_01_mangos_mail_loot_template required_8769_01_mangos_mail_level_reward bit; + +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'; diff --git a/sql/updates/8770_01_mangos_quest_template.sql b/sql/updates/8770_01_mangos_quest_template.sql new file mode 100644 index 000000000..ff82357af --- /dev/null +++ b/sql/updates/8770_01_mangos_quest_template.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_8769_01_mangos_mail_level_reward required_8770_01_mangos_quest_template bit; + +ALTER TABLE quest_template + CHANGE COLUMN QuestLevel QuestLevel smallint(6) NOT NULL DEFAULT 0; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 86d301969..a023a3bc4 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -145,6 +145,9 @@ pkgdata_DATA = \ 8726_01_mangos_spell_proc_event.sql \ 8728_01_realmd_account.sql \ 8731_01_mangos_creature_template.sql \ + 8749_01_mangos_mail_loot_template.sql \ + 8769_01_mangos_mail_level_reward.sql \ + 8770_01_mangos_quest_template.sql \ README ## Additional files to include when running 'make dist' @@ -270,4 +273,7 @@ EXTRA_DIST = \ 8726_01_mangos_spell_proc_event.sql \ 8728_01_realmd_account.sql \ 8731_01_mangos_creature_template.sql \ + 8749_01_mangos_mail_loot_template.sql \ + 8769_01_mangos_mail_level_reward.sql \ + 8770_01_mangos_quest_template.sql \ README diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index efcec8b1f..220b55ae7 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -1706,16 +1706,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 @@ -1734,7 +1724,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/ArenaTeam.cpp b/src/game/ArenaTeam.cpp index b9615a57c..891ce63c3 100644 --- a/src/game/ArenaTeam.cpp +++ b/src/game/ArenaTeam.cpp @@ -513,8 +513,8 @@ float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating) // ELO system if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) - if (enemy_rating < 1300) - enemy_rating = 1300; + if (enemy_rating < 1500) + enemy_rating = 1500; return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f)); } diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp index 605b10e8e..260b2931c 100644 --- a/src/game/AuctionHouseHandler.cpp +++ b/src/game/AuctionHouseHandler.cpp @@ -122,7 +122,9 @@ void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPri if (oldBidder) 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); } } @@ -142,7 +144,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); } } @@ -443,11 +447,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 15dbc47fd..bcf9a8f5e 100644 --- a/src/game/AuctionHouseMgr.cpp +++ b/src/game/AuctionHouseMgr.cpp @@ -141,16 +141,15 @@ 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); else RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! // will delete item or place to receiver mail list - 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); } // receiver not exist else @@ -187,7 +186,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); } } @@ -229,7 +229,9 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction ) 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, HOUR); + MailDraft(msgAuctionSuccessfulSubject.str(), itemTextId) + .AddMoney(profit) + .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_AUCTION, HOUR); } } @@ -261,11 +263,10 @@ void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry * auction ) else RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! - MailItemsInfo mi; - mi.AddItem(auction->item_guidlow, auction->item_template, pItem); - // will delete item or place to receiver mail list - 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); } // owner not found else diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 13d922800..f6709b3d9 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -933,10 +933,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(); @@ -951,7 +947,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 47786c4d2..2e738bd7f 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -431,6 +431,8 @@ ChatCommand * ChatHandler::getCommandTable() { "locales_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL }, { "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL }, { "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL }, + { "mail_level_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMailLevelRewardCommand, "", NULL }, + { "mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMailCommand, "", NULL }, { "mangos_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMangosStringCommand, "", NULL }, { "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL }, { "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL }, @@ -443,7 +445,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 271640e6c..b12f06f06 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -351,13 +351,14 @@ class ChatHandler bool HandleReloadLootTemplatesFishingCommand(const char* args); bool HandleReloadLootTemplatesGameobjectCommand(const char* args); bool HandleReloadLootTemplatesItemCommand(const char* args); + bool HandleReloadLootTemplatesMailCommand(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 HandleReloadMailLevelRewardCommand(const char* args); bool HandleReloadMangosStringCommand(const char* args); bool HandleReloadNpcGossipCommand(const char* args); bool HandleReloadNpcOptionCommand(const char* args); diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index a3c62b099..d2d2e9357 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -141,7 +141,7 @@ Creature::~Creature() void Creature::AddToWorld() { ///- Register the creature for guid lookup - if(!IsInWorld()) + if(!IsInWorld() && GetGUIDHigh()==HIGHGUID_UNIT) GetMap()->GetObjectsStore().insert(GetGUID(), (Creature*)this); Unit::AddToWorld(); @@ -150,7 +150,7 @@ void Creature::AddToWorld() void Creature::RemoveFromWorld() { ///- Remove the creature from the accessor - if(IsInWorld()) + if(IsInWorld() && GetGUIDHigh()==HIGHGUID_UNIT) GetMap()->GetObjectsStore().erase(GetGUID(), (Creature*)NULL); Unit::RemoveFromWorld(); @@ -574,7 +574,7 @@ bool Creature::AIM_Initialize() return true; } -bool Creature::Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data) +bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data) { ASSERT(map); SetMap(map); @@ -1353,7 +1353,17 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) } m_DBTableGuid = guid; - if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT); + if (map->GetInstanceId() == 0) + { + // Creature can be loaded already in map if grid has been unloaded while creature walk to another grid + // FIXME: until creature guids is global and for instances used dynamic generated guids + // in instance possible load creature duplicates with same DB guid but different in game guids + // This will be until implementing per-map creature guids + if (map->GetCreature(MAKE_NEW_GUID(guid,data->id,HIGHGUID_UNIT))) + return false; + } + else + guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT); uint16 team = 0; if(!Create(guid,map,data->phaseMask,data->id,team,data)) diff --git a/src/game/Creature.h b/src/game/Creature.h index 122af4d9f..cea73e59f 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -470,7 +470,7 @@ class MANGOS_DLL_SPEC Creature : public Unit void AddToWorld(); void RemoveFromWorld(); - bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data = NULL); + bool Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data = NULL); bool LoadCreaturesAddon(bool reload = false); void SelectLevel(const CreatureInfo *cinfo); void LoadEquipment(uint32 equip_entry, bool force=false); diff --git a/src/game/DBCStores.h b/src/game/DBCStores.h index 4a4761620..046cfc6b5 100644 --- a/src/game/DBCStores.h +++ b/src/game/DBCStores.h @@ -33,10 +33,11 @@ uint32 GetTalentSpellCost(uint32 spellId); TalentSpellPos const* GetTalentSpellPos(uint32 spellId); int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); uint32 GetAreaFlagByMapId(uint32 mapid); +MANGOS_DLL_SPEC AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); +MANGOS_DLL_SPEC AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); + uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); enum ContentLevels diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h index 372039021..1a09aa1d4 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -1069,7 +1069,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 d231c4cfe..8da251572 100644 --- a/src/game/DBCfmt.h +++ b/src/game/DBCfmt.h @@ -71,7 +71,7 @@ const char ItemRandomPropertiesfmt[]="nxiiiiissssssssssssssssx"; const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiiiiiiiii"; const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii"; const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; -const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; const char MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxixx"; const char MapDifficultyEntryfmt[]="diixxxxxxxxxxxxxxxxxiix"; const char MovieEntryfmt[]="nxx"; diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp index d1cd1452b..3d8791a4c 100644 --- a/src/game/GossipDef.cpp +++ b/src/game/GossipDef.cpp @@ -151,7 +151,7 @@ void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID ) data << uint32(questID); data << uint32(qItem.m_qIcon); - data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest)); + data << int32(pQuest->GetQuestLevel()); std::string Title = pQuest->GetTitle(); int loc_idx = pSession->GetSessionDbLocaleIndex(); @@ -400,7 +400,7 @@ void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Titl data << uint32(questID); data << uint32(qmi.m_qIcon); - data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest)); + data << int32(pQuest->GetQuestLevel()); data << title; } pSession->SendPacket( &data ); @@ -560,7 +560,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) data << uint32(pQuest->GetQuestId()); // quest id data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details) - data << uint32(pQuest->GetQuestLevel()); // may be 0, static data, in other cases must be used dynamic level: Player::GetQuestLevel + data << int32(pQuest->GetQuestLevel()); // may be -1, static data, in other cases must be used dynamic level: Player::GetQuestLevelForPlayer (0 is not known, but assuming this is no longer valid for quest intended for client) data << uint32(0); // min level data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp index d7917953e..a2e527360 100644 --- a/src/game/Guild.cpp +++ b/src/game/Guild.cpp @@ -951,15 +951,13 @@ void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid // Bank content related void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId) { - WorldPacket data(SMSG_GUILD_BANK_LIST,1200); - - GuildBankTab const* tab = GetBankTab(TabId); - if (!tab) - return; + GuildBankTab const* tab = m_TabListMap[TabId]; if (!IsMemberHaveRights(session->GetPlayer()->GetGUIDLow(),TabId,GUILD_BANK_RIGHT_VIEW_TAB)) return; + WorldPacket data(SMSG_GUILD_BANK_LIST,1200); + data << uint64(GetGuildBankMoney()); data << uint8(TabId); // remaining slots for today @@ -992,9 +990,7 @@ void Guild::DisplayGuildBankMoneyUpdate() void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2) { - GuildBankTab const* tab = GetBankTab(TabId); - if (!tab) - return; + GuildBankTab const* tab = m_TabListMap[TabId]; WorldPacket data(SMSG_GUILD_BANK_LIST,1200); @@ -1042,9 +1038,7 @@ void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2) void Guild::DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots) { - GuildBankTab const* tab = GetBankTab(TabId); - if (!tab) - return; + GuildBankTab const* tab = m_TabListMap[TabId]; WorldPacket data(SMSG_GUILD_BANK_LIST,1200); @@ -1134,14 +1128,6 @@ void Guild::CreateNewBankTab() void Guild::SetGuildBankTabInfo(uint8 TabId, std::string Name, std::string Icon) { - if (TabId >= GUILD_BANK_MAX_TABS) - return; - if (TabId >= m_TabListMap.size()) - return; - - if (!m_TabListMap[TabId]) - return; - if (m_TabListMap[TabId]->Name == Name && m_TabListMap[TabId]->Icon == Icon) return; @@ -1993,12 +1979,7 @@ void Guild::SetGuildBankTabText(uint8 TabId, std::string text) void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId) { - if (TabId > GUILD_BANK_MAX_TABS) - return; - - GuildBankTab const *tab = GetBankTab(TabId); - if (!tab) - return; + GuildBankTab const* tab = m_TabListMap[TabId]; WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1); data << uint8(TabId); diff --git a/src/game/Guild.h b/src/game/Guild.h index 4ef904536..ead843981 100644 --- a/src/game/Guild.h +++ b/src/game/Guild.h @@ -400,7 +400,6 @@ class Guild void SetGuildBankTabText(uint8 TabId, std::string text); void SendGuildBankTabText(WorldSession *session, uint8 TabId); void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon); - const GuildBankTab *GetBankTab(uint8 index) { if(index >= m_TabListMap.size()) return NULL; return m_TabListMap[index]; } uint8 GetPurchasedTabs() const { return m_PurchasedTabs; } uint32 GetBankRights(uint32 rankId, uint8 TabId) const; bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const; @@ -408,6 +407,7 @@ class Guild // Load/unload void LoadGuildBankFromDB(); void UnloadGuildBank(); + bool IsGuildBankLoaded() const { return m_GuildBankLoaded; } void IncOnlineMemberCount() { ++m_OnlineMembers; } // Money deposit/withdraw void SendMoneyInfo(WorldSession *session, uint32 LowGuid); diff --git a/src/game/GuildHandler.cpp b/src/game/GuildHandler.cpp index 550d761cd..723400ab9 100644 --- a/src/game/GuildHandler.cpp +++ b/src/game/GuildHandler.cpp @@ -859,7 +859,7 @@ void WorldSession::HandleGuildBankerActivate( WorldPacket & recv_data ) { if(Guild *pGuild = objmgr.GetGuildById(GuildId)) { - pGuild->DisplayGuildBankTabsInfo(this); + pGuild->DisplayGuildBankTabsInfo(this); // this also will load guild bank if not yet return; } } @@ -879,17 +879,21 @@ void WorldSession::HandleGuildBankQueryTab( WorldPacket & recv_data ) if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; - if(uint32 GuildId = GetPlayer()->GetGuildId()) - { - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - { - // Let's update the amount of gold the player can withdraw before displaying the content - // This is useful if money withdraw right has changed - pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow()); + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + return; - pGuild->DisplayGuildBankContent(this, TabId); - } - } + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild) + return; + + if (!pGuild->IsGuildBankLoaded() || TabId >= pGuild->GetPurchasedTabs()) + return; + + // Let's update the amount of gold the player can withdraw before displaying the content + // This is useful if money withdraw right has changed + pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow()); + pGuild->DisplayGuildBankContent(this, TabId); } void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data ) @@ -909,33 +913,38 @@ void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data ) if (GetPlayer()->GetMoney() < money) return; - if(uint32 GuildId = GetPlayer()->GetGuildId()) + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild) + return; + + if (!pGuild->IsGuildBankLoaded() || !pGuild->GetPurchasedTabs()) + return; + + CharacterDatabase.BeginTransaction(); + + pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money); + GetPlayer()->ModifyMoney(-int(money)); + GetPlayer()->SaveGoldToDB(); + + CharacterDatabase.CommitTransaction(); + + // logging money + if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - { - CharacterDatabase.BeginTransaction(); - - pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money); - GetPlayer()->ModifyMoney(-int(money)); - GetPlayer()->SaveGoldToDB(); - - CharacterDatabase.CommitTransaction(); - - // logging money - if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)", - _player->GetName(),_player->GetSession()->GetAccountId(),money,GuildId); - } - - // log - pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money); - - pGuild->DisplayGuildBankTabsInfo(this); - pGuild->DisplayGuildBankContent(this, 0); - pGuild->DisplayGuildBankMoneyUpdate(); - } + sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)", + _player->GetName(),_player->GetSession()->GetAccountId(),money,GuildId); } + + // log + pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money); + + pGuild->DisplayGuildBankTabsInfo(this); + pGuild->DisplayGuildBankContent(this, 0); + pGuild->DisplayGuildBankMoneyUpdate(); } void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data ) @@ -960,6 +969,9 @@ void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data ) if(!pGuild) return; + if (!pGuild->IsGuildBankLoaded() || !pGuild->GetPurchasedTabs()) + return; + if (pGuild->GetGuildBankMoney()> GoGuid >> BankToBank; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + { + recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet + return; + } + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild || !pGuild->IsGuildBankLoaded()) + { + recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet + return; + } + if (BankToBank) { recv_data >> BankTabDst; @@ -1016,7 +1043,10 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data ) recv_data >> unk2; // always 0 recv_data >> SplitedAmount; - if (BankTabSlotDst >= GUILD_BANK_MAX_SLOTS || (BankTabDst == BankTab && BankTabSlotDst == BankTabSlot)) + if (BankTabSlotDst >= GUILD_BANK_MAX_SLOTS || + (BankTabDst == BankTab && BankTabSlotDst == BankTabSlot) || + BankTab >= pGuild->GetPurchasedTabs() || + BankTabDst >= pGuild->GetPurchasedTabs()) { recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet return; @@ -1042,7 +1072,8 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data ) recv_data >> SplitedAmount; } - if (BankTabSlot >= GUILD_BANK_MAX_SLOTS && BankTabSlot != 0xFF) + if (BankTabSlot >= GUILD_BANK_MAX_SLOTS && BankTabSlot != 0xFF || + BankTab >= pGuild->GetPurchasedTabs()) { recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet return; @@ -1052,33 +1083,27 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data ) if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; - if(uint32 GuildId = GetPlayer()->GetGuildId()) + // Bank <-> Bank + if (BankToBank) { - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - { - // Bank <-> Bank - if (BankToBank) - { - pGuild->SwapItems(_player, BankTab, BankTabSlot, BankTabDst, BankTabSlotDst, SplitedAmount); - return; - } - - // Player <-> Bank - - // allow work with inventory only - if(!Player::IsInventoryPos(PlayerBag, PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) ) - { - _player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL ); - return; - } - - // BankToChar swap or char to bank remaining - if (ToChar) // Bank -> Char cases - pGuild->MoveFromBankToChar(_player, BankTab, BankTabSlot, PlayerBag, PlayerSlot, SplitedAmount); - else // Char -> Bank cases - pGuild->MoveFromCharToBank(_player, PlayerBag, PlayerSlot, BankTab, BankTabSlot, SplitedAmount); - } + pGuild->SwapItems(_player, BankTab, BankTabSlot, BankTabDst, BankTabSlotDst, SplitedAmount); + return; } + + // Player <-> Bank + + // allow work with inventory only + if(!Player::IsInventoryPos(PlayerBag, PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) ) + { + _player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL ); + return; + } + + // BankToChar swap or char to bank remaining + if (ToChar) // Bank -> Char cases + pGuild->MoveFromBankToChar(_player, BankTab, BankTabSlot, PlayerBag, PlayerSlot, SplitedAmount); + else // Char -> Bank cases + pGuild->MoveFromCharToBank(_player, PlayerBag, PlayerSlot, BankTab, BankTabSlot, SplitedAmount); } void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) @@ -1095,26 +1120,21 @@ void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) return; uint32 GuildId = GetPlayer()->GetGuildId(); - if (GuildId==0) + if (!GuildId) return; Guild *pGuild = objmgr.GetGuildById(GuildId); if(!pGuild) return; + // m_PurchasedTabs = 0 when buying Tab 0, that is why this check can be made + if (!pGuild->IsGuildBankLoaded() || TabId != pGuild->GetPurchasedTabs()) + return; + uint32 TabCost = GetGuildBankTabPrice(TabId) * GOLD; if (!TabCost) return; - if (pGuild->GetPurchasedTabs() >= GUILD_BANK_MAX_TABS) - return; - - if (TabId != pGuild->GetPurchasedTabs()) // m_PurchasedTabs = 0 when buying Tab 0, that is why this check can be made - { - sLog.outError("Error: trying to buy a tab non contiguous to owned ones"); - return; - } - if (GetPlayer()->GetMoney() < TabCost) // Should not happen, this is checked by client return; @@ -1149,15 +1169,20 @@ void WorldSession::HandleGuildBankUpdateTab( WorldPacket & recv_data ) if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; - if(uint32 GuildId = GetPlayer()->GetGuildId()) - { - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - { - pGuild->SetGuildBankTabInfo(TabId, Name, IconIndex); - pGuild->DisplayGuildBankTabsInfo(this); - pGuild->DisplayGuildBankContent(this, TabId); - } - } + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild) + return; + + if (!pGuild->IsGuildBankLoaded() || TabId >= pGuild->GetPurchasedTabs()) + return; + + pGuild->SetGuildBankTabInfo(TabId, Name, IconIndex); + pGuild->DisplayGuildBankTabsInfo(this); + pGuild->DisplayGuildBankContent(this, TabId); } void WorldSession::HandleGuildBankLogQuery( WorldPacket & recv_data ) @@ -1167,9 +1192,22 @@ void WorldSession::HandleGuildBankLogQuery( WorldPacket & recv_data ) uint8 TabId; recv_data >> TabId; - if(uint32 GuildId = GetPlayer()->GetGuildId()) - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - pGuild->DisplayGuildBankLogs(this, TabId); + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild) + return; + + if (!pGuild->IsGuildBankLoaded()) + return; + + // GUILD_BANK_MAX_TABS send by client for money log + if (TabId >= pGuild->GetPurchasedTabs() && TabId != GUILD_BANK_MAX_TABS) + return; + + pGuild->DisplayGuildBankLogs(this, TabId); } void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data) @@ -1179,9 +1217,18 @@ void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data) uint8 TabId; recv_data >> TabId; - if(uint32 GuildId = GetPlayer()->GetGuildId()) - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - pGuild->SendGuildBankTabText(this, TabId); + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild) + return; + + if (!pGuild->IsGuildBankLoaded() || TabId >= pGuild->GetPurchasedTabs()) + return; + + pGuild->SendGuildBankTabText(this, TabId); } void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data) @@ -1193,9 +1240,18 @@ void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data) recv_data >> TabId; recv_data >> Text; - if(uint32 GuildId = GetPlayer()->GetGuildId()) - if(Guild *pGuild = objmgr.GetGuildById(GuildId)) - pGuild->SetGuildBankTabText(TabId, Text); + uint32 GuildId = GetPlayer()->GetGuildId(); + if (!GuildId) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if (!pGuild) + return; + + if (!pGuild->IsGuildBankLoaded() || TabId >= pGuild->GetPurchasedTabs()) + return; + + pGuild->SetGuildBankTabText(TabId, Text); } void WorldSession::SendSaveGuildEmblem( uint32 msg ) diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index e65542d3e..b79283248 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -2003,13 +2003,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 c37723c22..c4d65f6bb 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -67,6 +67,7 @@ bool ChatHandler::HandleReloadAllCommand(const char*) HandleReloadAllItemCommand(""); HandleReloadAllLocalesCommand(""); + HandleReloadMailLevelRewardCommand(""); HandleReloadCommandCommand(""); HandleReloadReservedNameCommand(""); HandleReloadMangosStringCommand(""); @@ -360,12 +361,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(); - SendGlobalSysMessage("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; } @@ -822,6 +823,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; @@ -6216,25 +6225,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()); @@ -6278,13 +6285,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()); @@ -6387,4 +6394,4 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args) ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full, GetNameLink().c_str()); return true; -} +} \ No newline at end of file diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index 343a93074..203bc24df 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -35,17 +35,17 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT }; -LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true); -LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true); -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_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_Creature( "creature_loot_template", "creature entry", true); +LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true); +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_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); class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed) @@ -1256,28 +1256,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() @@ -1354,7 +1345,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 b8ce00e9a..a9e5a7c44 100644 --- a/src/game/LootMgr.h +++ b/src/game/LootMgr.h @@ -324,24 +324,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(); @@ -352,12 +352,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 3da8d068b..24463acf8 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -29,6 +29,8 @@ #include "Language.h" #include "DBCStores.h" #include "BattleGroundMgr.h" +#include "Item.h" +#include "AuctionHouseMgr.h" enum MailShowFlags { @@ -39,18 +41,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; @@ -60,9 +50,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; @@ -70,34 +57,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(); // 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; @@ -105,7 +90,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) @@ -118,7 +103,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; @@ -139,7 +124,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(); @@ -147,8 +132,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 +154,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); - if (items_count) + Item* items[MAX_MAIL_ITEMS]; + + 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; - - if(!mailItem.item_guidlow) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); - return; - } - - 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; - } - - if(!mailItem.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 (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(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); - return; - } + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); + return; } + + Item* item = pl->GetItemByGuid(itemGUIDs[i]); + + // 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 (!item->CanBeTraded(true)) + { + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); + 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 (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 && 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 +254,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(); @@ -314,7 +295,7 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data ) uint32 mailId; recv_data >> mailbox; recv_data >> mailId; - recv_data.read_skip(); // 3.3.0 + recv_data.read_skip(); // mailTemplateId if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; @@ -341,11 +322,12 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) uint64 mailbox; uint32 mailId; recv_data >> mailbox; + recv_data >> mailId; + recv_data.read_skip(); // 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)) @@ -362,77 +344,36 @@ 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::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::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); } - } - SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId); + 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 ) { @@ -503,7 +444,9 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) // check player existence 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) ); @@ -608,7 +551,7 @@ 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 << uint16(next_mail_size); // Message size data << uint32((*itr)->messageID); // Message ID data << uint8((*itr)->messageType); // Message Type @@ -710,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(); // mailTemplateId, non need, Mail store own 100% correct value anyway if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; @@ -718,12 +663,27 @@ 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)) { @@ -731,7 +691,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) return; } - bodyItem->SetUInt32Value( ITEM_FIELD_ITEM_TEXT_ID, m->itemTextId ); + bodyItem->SetUInt32Value( ITEM_FIELD_ITEM_TEXT_ID, itemTextId ); bodyItem->SetUInt32Value( ITEM_FIELD_CREATOR, m->sender); sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId); @@ -813,90 +773,217 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) 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 (!m_mailTemplateId || !m_mailTemplateItemsNeed) + return; + + m_mailTemplateItemsNeed = false; + + Loot mailLoot; + + mailLoot.FillLoot(m_mailTemplateId, LootTemplates_Mail, receiver,true); + + uint32 max_slot = mailLoot.GetMaxSlotInLootFor(receiver); + for(uint32 i = 0; m_items.size() < MAX_MAIL_ITEMS && i < max_slot; ++i) + { + if (LootItem* lootitem = mailLoot.LootItemInSlot(i,receiver)) + { + 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(); time_t deliver_time = time(NULL) + deliver_delay; uint32 expire_delay; // auction mail without any items and money (auction sale note) pending 1 hour - if (messageType == MAIL_AUCTION && !mi && !money) + if (sender.GetMailMessageType() == MAIL_AUCTION && m_items.empty() && !m_money) expire_delay = HOUR; // mail from battlemaster (rewardmarks) should last only one day - else if (messageType == MAIL_CREATURE && sBattleGroundMgr.GetBattleMasterBG(sender_guidlow_or_entry) != BATTLEGROUND_TYPE_NONE) + 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 (!m_items.empty()) + 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); - } - } - CharacterDatabase.CommitTransaction(); + else if (!m_items.empty()) + deleteIncludedItems(); } diff --git a/src/game/Mail.h b/src/game/Mail.h index c13124c04..b46370e63 100644 --- a/src/game/Mail.h +++ b/src/game/Mail.h @@ -21,20 +21,12 @@ #include "Common.h" #include +struct AuctionEntry; class Item; #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, @@ -62,16 +54,75 @@ enum MailAuctionAnswers AUCTION_SALE_PENDING = 6 }; -// gathered from Stationery.dbc -enum MailStationery +class MailSender { - MAIL_STATIONERY_UNKNOWN = 1, - MAIL_STATIONERY_NORMAL = 41, - MAIL_STATIONERY_GM = 61, - MAIL_STATIONERY_AUCTION = 62, - MAIL_STATIONERY_VAL = 64, - MAIL_STATIONERY_CHR = 65, - MAIL_STATIONERY_ORP = 67, // new in 3.2.2 + 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; +}; + +class MailReceiver +{ + 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; +}; + +class MailDraft +{ + typedef std::map 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 + + uint16 m_mailTemplateId; + bool m_mailTemplateItemsNeed; + std::string m_subject; + uint32 m_bodyId; + + MailItemMap m_items; // Keep the items in a map to avoid duplicate guids (which can happen), store only low part of guid + + uint32 m_money; + uint32 m_COD; }; struct MailItemInfo @@ -80,61 +131,6 @@ struct MailItemInfo uint32 item_template; }; -struct MailItem -{ - MailItem() : item_slot(0), item_guidlow(0), item_template(0), item(NULL) {} - - uint8 item_slot; // slot in mail - uint32 item_guidlow; // item guid (low part) - uint32 item_template; // item entry - Item *item; // item pointer - - void deleteItem(bool inDB = false); -}; - -typedef std::map MailItemMap; - -class MailItemsInfo -{ - 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 -}; - struct Mail { uint32 messageID; @@ -162,15 +158,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::iterator itr = items.begin(); itr != items.end(); ++itr) @@ -186,4 +173,5 @@ struct Mail bool HasItems() const { return !items.empty(); } }; + #endif diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp index 1455adf7e..852486ddf 100644 --- a/src/game/MapInstanced.cpp +++ b/src/game/MapInstanced.cpp @@ -182,22 +182,19 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save, Guard guard(*this); // make sure we have a valid map id - const MapEntry* entry = sMapStore.LookupEntry(GetId()); - if(!entry) + if (!sMapStore.LookupEntry(GetId())) { sLog.outError("CreateInstance: no entry for map %d", GetId()); assert(false); } - const InstanceTemplate * iTemplate = objmgr.GetInstanceTemplate(GetId()); - if(!iTemplate) + if (!objmgr.GetInstanceTemplate(GetId())) { sLog.outError("CreateInstance: no instance template for map %d", GetId()); assert(false); } // some instances only have one difficulty - MapDifficulty const* mapDiff = GetMapDifficultyData(GetId(),difficulty); - if (!mapDiff) + if (!GetMapDifficultyData(GetId(),difficulty)) difficulty = DUNGEON_DIFFICULTY_NORMAL; sLog.outDebug("MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp index 1282a7b2d..b2096c074 100644 --- a/src/game/ObjectGridLoader.cpp +++ b/src/game/ObjectGridLoader.cpp @@ -104,17 +104,6 @@ template<> void addUnitState(Creature *obj, CellPair const& cell_pair) obj->SetCurrentCell(cell); } -template bool alreadyLoaded(Map* /*map*/, uint32 /*guid*/) -{ - // Non creature objects not walk by grids - return false; -} - -template<> bool alreadyLoaded(Map* map, uint32 guid) -{ - return map->GetObjectsStore().find(guid,(Creature*)NULL); -} - template void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager &m, uint32 &count, Map* map) { @@ -124,13 +113,6 @@ void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager & { uint32 guid = *i_guid; - // Note: this will fully correct work only at non-instanced maps, - // at instanced maps will use dynamic selected guid - // and then duplicate just will not detected and will be 2 creature with identical DB guid - // with some chance - if (alreadyLoaded(map,guid)) - continue; // still loaded in another grid (move from respawn [this] grid early), we not need second copy - T* obj = new T; //sLog.outString("DEBUG: LoadHelper from table: %s for (guid: %u) Loading",table,guid); if(!obj->LoadFromDB(guid, map)) diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 358f12ab4..f8f087ab1 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -2163,10 +2163,11 @@ void ObjectMgr::LoadPetLevelInfo() if (!result) { barGoLink bar( 1 ); + bar.step(); sLog.outString(); - sLog.outString( ">> Loaded %u level pet stats definitions", count ); - sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table."); + sLog.outString(">> Loaded %u level pet stats definitions", count); + sLog.outErrorDb("Error loading `pet_levelstats` table or empty table."); return; } @@ -3278,6 +3279,9 @@ void ObjectMgr::LoadQuests() delete result; // Post processing + + std::map usedMailTemplates; + for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter) { Quest * qinfo = iter->second; @@ -3766,21 +3770,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::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 ); @@ -3791,7 +3805,7 @@ void ObjectMgr::LoadQuests() } // fill additional data stores - if(qinfo->PrevQuestId) + if (qinfo->PrevQuestId) { if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end()) { @@ -7504,6 +7518,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 c58f196ec..b888e39b8 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -179,6 +179,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 MailLevelRewardList; +typedef UNORDERED_MAP MailLevelRewardMap; + struct ReputationOnKillEntry { uint32 repfaction1; @@ -515,6 +528,7 @@ class ObjectMgr void LoadNpcOptionLocales(); void LoadPointOfInterestLocales(); void LoadInstanceTemplate(); + void LoadMailLevelRewards(); void LoadGossipText(); @@ -584,6 +598,19 @@ class ObjectMgr typedef std::multimap 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); @@ -844,6 +871,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 PetLevelInfoMap; // PetLevelInfoMap[creature_id][level] PetLevelInfoMap petInfo; // [creature_id][level] diff --git a/src/game/Player.cpp b/src/game/Player.cpp index f319b8ca4..a7fe5ea17 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -2452,6 +2452,9 @@ void Player::GiveLevel(uint32 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); } @@ -3914,8 +3917,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 @@ -3923,18 +3926,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 @@ -3963,7 +3978,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC continue; } - mi.AddItem(item_guidlow, item_template, pItem); + draft.AddItem(pItem); } while (resultItems->NextRow()); @@ -3975,7 +3990,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()); @@ -4816,56 +4831,6 @@ float Player::GetRatingBonusValue(CombatRating cr) const return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) / GetRatingCoefficient(cr); } -uint32 Player::GetMeleeCritDamageReduction(uint32 damage) const -{ - float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*2.2f; - if (melee>33.0f) melee = 33.0f; - return uint32 (melee * damage /100.0f); -} - -uint32 Player::GetMeleeDamageReduction(uint32 damage) const -{ - float rate = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE); - // Resilience not limited (limit it by 100%) - if (rate > 100.0f) - rate = 100.0f; - return uint32 (rate * damage / 100.0f); -} - -uint32 Player::GetRangedCritDamageReduction(uint32 damage) const -{ - float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.2f; - if (ranged>33.0f) ranged=33.0f; - return uint32 (ranged * damage /100.0f); -} - -uint32 Player::GetRangedDamageReduction(uint32 damage) const -{ - float rate = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED); - // Resilience not limited (limit it by 100%) - if (rate > 100.0f) - rate = 100.0f; - return uint32 (rate * damage / 100.0f); -} - -uint32 Player::GetSpellCritDamageReduction(uint32 damage) const -{ - float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.2f; - // In wow script resilience limited to 33% - if (spell>33.0f) - spell = 33.0f; - return uint32 (spell * damage / 100.0f); -} - -uint32 Player::GetSpellDamageReduction(uint32 damage) const -{ - float rate = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL); - // Resilience not limited (limit it by 100%) - if (rate > 100.0f) - rate = 100.0f; - return uint32 (rate * damage / 100.0f); -} - float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const { switch (attType) @@ -5952,7 +5917,7 @@ void Player::RewardReputation(Quest const *pQuest) { if(pQuest->RewRepFaction[i] && pQuest->RewRepValue[i] ) { - int32 rep = CalculateReputationGain(GetQuestLevel(pQuest), pQuest->RewRepValue[i], pQuest->RewRepFaction[i], true); + int32 rep = CalculateReputationGain(GetQuestLevelForPlayer(pQuest), pQuest->RewRepValue[i], pQuest->RewRepFaction[i], true); FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]); if(factionEntry) GetReputationMgr().ModifyReputation(factionEntry, rep); @@ -12764,56 +12729,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()) { @@ -15040,20 +14957,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(int 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()->GetMangosString(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()) @@ -18921,8 +18838,6 @@ void Player::AutoUnequipOffhandIfNeed() } 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 @@ -18930,7 +18845,7 @@ void Player::AutoUnequipOffhandIfNeed() 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)); } } @@ -20806,4 +20721,4 @@ void Player::SendDuelCountdown(uint32 counter) WorldPacket data(SMSG_DUEL_COUNTDOWN, 4); data << uint32(counter); // seconds GetSession()->SendPacket(&data); -} +} \ No newline at end of file diff --git a/src/game/Player.h b/src/game/Player.h index e9f13ca18..eed7470f2 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1281,7 +1281,8 @@ class MANGOS_DLL_SPEC Player : public Unit /*** QUEST SYSTEM ***/ /*********************************************************/ - uint32 GetQuestLevel( Quest const* pQuest ) const { return pQuest && pQuest->GetQuestLevel() ? pQuest->GetQuestLevel() : getLevel(); } + // Return player level when QuestLevel is dynamic (-1) + uint32 GetQuestLevelForPlayer(Quest const* pQuest) const { return pQuest && (pQuest->GetQuestLevel() > 0) ? (uint32)pQuest->GetQuestLevel() : getLevel(); } void PrepareQuestMenu( uint64 guid ); void SendPreparedQuest( uint64 guid ); @@ -1298,6 +1299,7 @@ class MANGOS_DLL_SPEC Player : public Unit void CompleteQuest( uint32 quest_id ); void IncompleteQuest( uint32 quest_id ); void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true ); + void FailQuest( uint32 quest_id ); bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg ); bool SatisfyQuestLevel( Quest const* qInfo, bool msg ); @@ -1731,12 +1733,6 @@ class MANGOS_DLL_SPEC Player : public Unit float OCTRegenMPPerSpirit(); float GetRatingCoefficient(CombatRating cr) const; float GetRatingBonusValue(CombatRating cr) const; - uint32 GetMeleeCritDamageReduction(uint32 damage) const; - uint32 GetMeleeDamageReduction(uint32 damage) const; - uint32 GetRangedCritDamageReduction(uint32 damage) const; - uint32 GetRangedDamageReduction(uint32 damage) const; - uint32 GetSpellCritDamageReduction(uint32 damage) const; - uint32 GetSpellDamageReduction(uint32 damage) const; uint32 GetBaseSpellPowerBonus() { return m_baseSpellPower; } float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const; diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp index 5a53e5252..b9c068f86 100644 --- a/src/game/QuestDef.cpp +++ b/src/game/QuestDef.cpp @@ -27,7 +27,7 @@ Quest::Quest(Field * questRecord) ZoneOrSort = questRecord[2].GetInt32(); SkillOrClass = questRecord[3].GetInt32(); MinLevel = questRecord[4].GetUInt32(); - QuestLevel = questRecord[5].GetUInt32(); + QuestLevel = questRecord[5].GetInt32(); Type = questRecord[6].GetUInt32(); RequiredRaces = questRecord[7].GetUInt32(); RequiredSkillValue = questRecord[8].GetUInt32(); @@ -169,7 +169,7 @@ uint32 Quest::XPValue( Player *pPlayer ) const if( RewMoneyMaxLevel > 0 ) { uint32 pLevel = pPlayer->getLevel(); - uint32 qLevel = QuestLevel; + uint32 qLevel = QuestLevel > 0 ? (uint32)QuestLevel : 0; float fullxp = 0; if (qLevel >= 15) fullxp = RewMoneyMaxLevel / 6.0f; diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index 546a5dcc5..54e95a497 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -181,7 +181,7 @@ class Quest int32 GetZoneOrSort() const { return ZoneOrSort; } int32 GetSkillOrClass() const { return SkillOrClass; } uint32 GetMinLevel() const { return MinLevel; } - uint32 GetQuestLevel() const { return QuestLevel; } + int32 GetQuestLevel() const { return QuestLevel; } uint32 GetType() const { return Type; } uint32 GetRequiredRaces() const { return RequiredRaces; } uint32 GetRequiredSkillValue() const { return RequiredSkillValue; } @@ -274,7 +274,7 @@ class Quest int32 ZoneOrSort; int32 SkillOrClass; uint32 MinLevel; - uint32 QuestLevel; + int32 QuestLevel; uint32 Type; uint32 RequiredRaces; uint32 RequiredSkillValue; diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp index a93b65880..6c6c4cb76 100644 --- a/src/game/QuestHandler.cpp +++ b/src/game/QuestHandler.cpp @@ -606,7 +606,7 @@ uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32 { if ( pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->getQuestStatusMap()[quest_id].m_rewarded)) result2 = DIALOG_STATUS_REWARD_REP; - else if (pPlayer->getLevel() <= pPlayer->GetQuestLevel(pQuest) + sWorld.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF) ) + else if (pPlayer->getLevel() <= pPlayer->GetQuestLevelForPlayer(pQuest) + sWorld.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF) ) { if (pQuest->HasFlag(QUEST_FLAGS_DAILY)) result2 = DIALOG_STATUS_AVAILABLE_REP; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index a96b80a8c..279391595 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -2387,10 +2387,10 @@ enum PetDiet #define CHAIN_SPELL_JUMP_RADIUS 10 // Max values for Guild & Guild Bank -#define GUILD_BANK_MAX_TABS 6 +#define GUILD_BANK_MAX_TABS 6 // send by client for money log also #define GUILD_BANK_MAX_SLOTS 98 #define GUILD_BANK_MAX_LOGS 25 -#define GUILD_BANK_MONEY_LOGS_TAB 100 +#define GUILD_BANK_MONEY_LOGS_TAB 100 // used for money log in DB #define GUILD_EVENTLOG_MAX_RECORDS 100 #define GUILD_RANKS_MIN_COUNT 5 #define GUILD_RANKS_MAX_COUNT 10 @@ -2620,6 +2620,27 @@ enum BattleGroundTypeId }; #define MAX_BATTLEGROUND_TYPE_ID 33 +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, + MAIL_STATIONERY_ORP = 67, // new in 3.2.2 +}; + enum MailResponseType { MAIL_SEND = 0, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 7126a1ad9..8af72353e 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -6382,12 +6382,12 @@ void Aura::PeriodicTick() if (isCrit) cleanDamage.hitOutCome = MELEE_HIT_CRIT; - // Reduce dot damage from resilience for players. + // only from players // FIXME: need use SpellDamageBonus instead? - if (m_target->GetTypeId() == TYPEID_PLAYER) - pdamage-=((Player*)m_target)->GetSpellDamageReduction(pdamage); + if (IS_PLAYER_GUID(m_caster_guid)) + pdamage -= m_target->GetSpellDamageReduction(pdamage); - pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist); + pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist, !(GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED)); sLog.outDetail("PeriodicTick: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb); @@ -6442,7 +6442,7 @@ void Aura::PeriodicTick() pdamage = pCaster->SpellDamageBonus(m_target, GetSpellProto(), pdamage, DOT, GetStackAmount()); - pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist); + pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist, !(GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED)); if(m_target->GetHealth() < pdamage) pdamage = uint32(m_target->GetHealth()); @@ -6609,8 +6609,8 @@ void Aura::PeriodicTick() int32 drain_amount = m_target->GetPower(power) > pdamage ? pdamage : m_target->GetPower(power); // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) - if (power == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER) - drain_amount -= ((Player*)m_target)->GetSpellCritDamageReduction(drain_amount); + if (power == POWER_MANA) + drain_amount -= m_target->GetSpellCritDamageReduction(drain_amount); m_target->ModifyPower(power, -drain_amount); @@ -6701,8 +6701,8 @@ void Aura::PeriodicTick() return; // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) - if (powerType == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER) - pdamage -= ((Player*)m_target)->GetSpellCritDamageReduction(pdamage); + if (powerType == POWER_MANA) + pdamage -= m_target->GetSpellCritDamageReduction(pdamage); uint32 gain = uint32(-m_target->ModifyPower(powerType, -pdamage)); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index bfe3eca10..c25d3946e 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -2486,8 +2486,8 @@ void Spell::EffectPowerDrain(uint32 i) // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) uint32 power = damage; - if ( drain_power == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER ) - power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); + if (drain_power == POWER_MANA) + power -= unitTarget->GetSpellCritDamageReduction(power); int32 new_damage; if(curPower < power) @@ -2550,8 +2550,8 @@ void Spell::EffectPowerBurn(uint32 i) // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) uint32 power = damage; - if (powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER) - power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); + if (powertype == POWER_MANA) + power -= unitTarget->GetSpellCritDamageReduction(power); int32 new_damage = (curPower < power) ? curPower : power; diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index a7d6a56f9..b475706f1 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -1060,11 +1060,11 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); // Resilience - reduce crit damage - if (pVictim->GetTypeId()==TYPEID_PLAYER) - { - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); - damage -= ((Player*)pVictim)->GetMeleeCritDamageReduction(redunction_affected_damage); - } + uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); + if (attackType != RANGED_ATTACK) + damage -= pVictim->GetMeleeCritDamageReduction(redunction_affected_damage); + else + damage -= pVictim->GetRangedCritDamageReduction(redunction_affected_damage); } } break; @@ -1080,20 +1080,18 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); // Resilience - reduce crit damage - if (pVictim->GetTypeId()==TYPEID_PLAYER) - { - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); - damage -= ((Player*)pVictim)->GetSpellCritDamageReduction(redunction_affected_damage); - } + uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); + damage -= pVictim->GetSpellCritDamageReduction(redunction_affected_damage); } } break; } - if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + // only from players + if (GetTypeId() == TYPEID_PLAYER) { uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); - damage -= ((Player*)pVictim)->GetSpellDamageReduction(redunction_affected_damage); + damage -= pVictim->GetSpellDamageReduction(redunction_affected_damage); } // damage mitigation @@ -1116,7 +1114,7 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S } uint32 absorb_affected_damage = CalcNotIgnoreAbsorbDamage(damage,damageSchoolMask,spellInfo); - CalcAbsorbResist(pVictim, damageSchoolMask, SPELL_DIRECT_DAMAGE, absorb_affected_damage, &damageInfo->absorb, &damageInfo->resist); + CalcAbsorbResist(pVictim, damageSchoolMask, SPELL_DIRECT_DAMAGE, absorb_affected_damage, &damageInfo->absorb, &damageInfo->resist, !(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED)); damage-= damageInfo->absorb + damageInfo->resist; } else @@ -1286,13 +1284,15 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f)); // Resilience - reduce crit damage - if (pVictim->GetTypeId()==TYPEID_PLAYER) - { - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damageInfo->damage,damageInfo->damageSchoolMask); - uint32 resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(redunction_affected_damage); - damageInfo->damage -= resilienceReduction; - damageInfo->cleanDamage += resilienceReduction; - } + uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damageInfo->damage,damageInfo->damageSchoolMask); + uint32 resilienceReduction; + if (attackType != RANGED_ATTACK) + resilienceReduction = pVictim->GetMeleeCritDamageReduction(redunction_affected_damage); + else + resilienceReduction = pVictim->GetRangedCritDamageReduction(redunction_affected_damage); + + damageInfo->damage -= resilienceReduction; + damageInfo->cleanDamage += resilienceReduction; break; } case MELEE_HIT_PARRY: @@ -1391,13 +1391,14 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da break; } - if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + // only from players + if (GetTypeId() == TYPEID_PLAYER) { uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageInfo->damageSchoolMask); if (attackType != RANGED_ATTACK) - damage-=((Player*)pVictim)->GetMeleeDamageReduction(redunction_affected_damage); + damage -= pVictim->GetMeleeDamageReduction(redunction_affected_damage); else - damage-=((Player*)pVictim)->GetRangedDamageReduction(redunction_affected_damage); + damage -= pVictim->GetRangedDamageReduction(redunction_affected_damage); } // Calculate absorb resist @@ -1407,7 +1408,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da // Calculate absorb & resists uint32 absorb_affected_damage = CalcNotIgnoreAbsorbDamage(damageInfo->damage,damageInfo->damageSchoolMask); - CalcAbsorbResist(damageInfo->target, damageInfo->damageSchoolMask, DIRECT_DAMAGE, absorb_affected_damage, &damageInfo->absorb, &damageInfo->resist); + CalcAbsorbResist(damageInfo->target, damageInfo->damageSchoolMask, DIRECT_DAMAGE, absorb_affected_damage, &damageInfo->absorb, &damageInfo->resist, true); damageInfo->damage-=damageInfo->absorb + damageInfo->resist; if (damageInfo->absorb) { @@ -1627,7 +1628,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage) return (newdamage > 1) ? newdamage : 1; } -void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist) +void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, bool canReflect) { if(!pVictim || !pVictim->isAlive() || !damage) return; @@ -1731,7 +1732,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe continue; } // Reflective Shield (Lady Malande boss) - if (spellProto->Id == 41475) + if (spellProto->Id == 41475 && canReflect) { if(RemainingDamage < currentAbsorb) reflectDamage = RemainingDamage / 2; @@ -1789,7 +1790,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe } // Reflective Shield - if (spellProto->SpellFamilyFlags == 0x1) + if (spellProto->SpellFamilyFlags == 0x1 && canReflect) { if (pVictim == this) break; @@ -1911,7 +1912,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe } // Cast back reflect damage spell - if (reflectSpell) + if (canReflect && reflectSpell) pVictim->CastCustomSpell(this, reflectSpell, &reflectDamage, NULL, NULL, true, NULL, reflectTriggeredBy); // absorb by mana cost @@ -2941,13 +2942,10 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVict crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); // reduce crit chance from Rating for players - if (pVictim->GetTypeId()==TYPEID_PLAYER) - { - if (attackType==RANGED_ATTACK) - crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_RANGED); - else - crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE); - } + if (attackType != RANGED_ATTACK) + crit -= pVictim->GetMeleeCritChanceReduction(); + else + crit -= pVictim->GetRangedCritChanceReduction(); // Apply crit chance from defence skill crit += (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f; @@ -8622,8 +8620,7 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); // Modify by player victim resilience - if (pVictim->GetTypeId() == TYPEID_PLAYER) - crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL); + crit_chance -= pVictim->GetSpellCritChanceReduction(); } // scripted (increase crit chance ... against ... target by x%) @@ -9626,7 +9623,8 @@ bool Unit::isTargetableForAttack(bool inverseAlive /*=false*/) const if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) return false; - if ((isAlive() && !isInvisibleForAlive()) == inverseAlive) + // inversealive is needed for some spells which need to be casted at dead targets (aoe) + if (isAlive() == inverseAlive) return false; return IsInWorld() && !hasUnitState(UNIT_STAT_DIED) && !isInFlight(); @@ -9788,8 +9786,11 @@ bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo return false; } - if (u->isAlive() && isInvisibleForAlive()) - if (u->GetTypeId() == TYPEID_PLAYER && !((Player *)u)->isGameMaster()) + // isInvisibleForAlive() those units can only be seen by dead or if other + // unit is also invisible for alive.. if an isinvisibleforalive unit dies we + // should be able to see it too + if (u->isAlive() && isAlive() && isInvisibleForAlive() != u->isInvisibleForAlive()) + if (u->GetTypeId() != TYPEID_PLAYER || !((Player *)u)->isGameMaster()) return false; // Visible units, always are visible for all units, except for units under invisibility and phases @@ -12631,3 +12632,26 @@ void Unit::KnockBackFrom(Unit* target, float horizintalSpeed, float verticalSpee NearTeleportTo(fx, fy, fz, GetOrientation(), this == target); } } + +float Unit::GetCombatRatingReduction(CombatRating cr) const +{ + if (GetTypeId() == TYPEID_PLAYER) + return ((Player const*)this)->GetRatingBonusValue(cr); + else if (((Creature const*)this)->isPet()) + { + // Player's pet have 0.4 resilience from owner + if (Unit* owner = GetOwner()) + if(owner->GetTypeId() == TYPEID_PLAYER) + return ((Player*)owner)->GetRatingBonusValue(cr) * 0.4f; + } + + return 0.0f; +} + +uint32 Unit::GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const +{ + float percent = GetCombatRatingReduction(cr) * rate; + if (percent > cap) + percent = cap; + return uint32 (percent * damage / 100.0f); +} diff --git a/src/game/Unit.h b/src/game/Unit.h index 9dcb55c7a..b481c3771 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1034,6 +1034,21 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK); void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss); + // player or player's pet resilience (-1%) + float GetMeleeCritChanceReduction() const { return GetCombatRatingReduction(CR_CRIT_TAKEN_MELEE); } + float GetRangedCritChanceReduction() const { return GetCombatRatingReduction(CR_CRIT_TAKEN_RANGED); } + float GetSpellCritChanceReduction() const { return GetCombatRatingReduction(CR_CRIT_TAKEN_SPELL); } + + // player or player's pet resilience (-1%) + uint32 GetMeleeCritDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_MELEE, 2.2f, 33.0f, damage); } + uint32 GetRangedCritDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_RANGED, 2.2f, 33.0f, damage); } + uint32 GetSpellCritDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_SPELL, 2.2f, 33.0f, damage); } + + // player or player's pet resilience (-1%), cap 100% + uint32 GetMeleeDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_MELEE, 1.0f, 100.0f, damage); } + uint32 GetRangedDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_MELEE, 1.0f, 100.0f, damage); } + uint32 GetSpellDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_MELEE, 1.0f, 100.0f, damage); } + float MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell); SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell); SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell); @@ -1462,7 +1477,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject // redefined in Creature uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage); - void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist); + void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, bool canReflect = false); void UpdateSpeed(UnitMoveType mtype, bool forced); float GetSpeed( UnitMoveType mtype ) const; @@ -1583,6 +1598,10 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 cooldown); bool HandleMendingAuraProc(Aura* triggeredByAura); + // player or player's pet + float GetCombatRatingReduction(CombatRating cr) const; + uint32 GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const; + uint32 m_state; // Even derived shouldn't modify uint32 m_CombatTimer; diff --git a/src/game/World.cpp b/src/game/World.cpp index 6685a3d92..8efd7bf0f 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1106,6 +1106,9 @@ void World::SetInitialWorldSettings() ///- Initialize the random number generator srand((unsigned int)time(NULL)); + ///- Time server startup + uint32 uStartTime = getMSTime(); + ///- Initialize config settings LoadConfigSettings(); @@ -1335,6 +1338,9 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Player Corpses..." ); objmgr.LoadCorpses(); + sLog.outString( "Loading Player level dependent mail rewards..." ); + objmgr.LoadMailLevelRewards(); + sLog.outString( "Loading Loot Tables..." ); sLog.outString(); LoadLootTables(); @@ -1508,6 +1514,9 @@ void World::SetInitialWorldSettings() m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event sLog.outString( "WORLD: World initialized" ); + + uint32 uStartInterval = getMSTimeDiff(uStartTime, getMSTime()); + sLog.outString( "SERVER STARTUP TIME: %i minutes %i seconds", uStartInterval / 60000, (uStartInterval % 60000) / 1000 ); } void World::DetectDBCLang() diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index afd9a1e41..f26fb3bea 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -26,7 +26,6 @@ #include "Common.h" #include "SharedDefines.h" -class MailItemsInfo; struct ItemPrototype; struct AuctionEntry; struct DeclinedName; @@ -39,7 +38,6 @@ class Player; class Unit; class WorldPacket; class WorldSocket; -class WorldSession; class QueryResult; class LoginQueryHolder; class CharacterHandler; @@ -220,12 +218,8 @@ class MANGOS_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 ); diff --git a/src/mangosd/Makefile.am b/src/mangosd/Makefile.am index 608d0a1ba..3fd406888 100644 --- a/src/mangosd/Makefile.am +++ b/src/mangosd/Makefile.am @@ -43,10 +43,9 @@ mangos_worldd_LDADD = \ ../shared/vmap/libmangosvmaps.a \ ../framework/libmangosframework.a \ ../../dep/src/sockets/libmangossockets.a \ - ../../dep/src/g3dlite/libg3dlite.a \ - ../../dep/tbb/libtbbmalloc.so + ../../dep/src/g3dlite/libg3dlite.a -mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/g3dlite -L../bindings/universal/ -L../../dep/tbb -L$(libdir) $(MANGOS_LIBS) -export-dynamic +mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/g3dlite -L../bindings/universal/ -L$(libdir) $(MANGOS_LIBS) -export-dynamic ## Additional files to include when running 'make dist' # Include world daemon configuration diff --git a/src/realmd/Makefile.am b/src/realmd/Makefile.am index 4969c4082..6aa09c392 100644 --- a/src/realmd/Makefile.am +++ b/src/realmd/Makefile.am @@ -36,10 +36,9 @@ mangos_realmd_LDADD = \ ../shared/Auth/libmangosauth.a \ ../shared/libmangosshared.a \ ../framework/libmangosframework.a \ - ../../dep/src/sockets/libmangossockets.a \ - ../../dep/tbb/libtbbmalloc.so + ../../dep/src/sockets/libmangossockets.a -mangos_realmd_LDFLAGS = -L../../dep/src/sockets -L../../dep/tbb -L$(libdir) $(MANGOS_LIBS) +mangos_realmd_LDFLAGS = -L../../dep/src/sockets -L$(libdir) $(MANGOS_LIBS) ## Additional files to include when running 'make dist' # Include realm list daemon configuration diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 1b1973540..24ab70cac 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "8745" + #define REVISION_NR "8770" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index ed97f2ba0..b658c51c9 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_8721_01_characters_guild" - #define REVISION_DB_MANGOS "required_8731_01_mangos_creature_template" + #define REVISION_DB_MANGOS "required_8770_01_mangos_quest_template" #define REVISION_DB_REALMD "required_8728_01_realmd_account" #endif // __REVISION_SQL_H__ diff --git a/win/VC100/framework.vcxproj b/win/VC100/framework.vcxproj index 1eafcd3bd..90ff8548e 100644 --- a/win/VC100/framework.vcxproj +++ b/win/VC100/framework.vcxproj @@ -143,7 +143,7 @@ /Zl /MP %(AdditionalOptions) Disabled ..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories) - WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebugDLL @@ -170,7 +170,7 @@ /Zl /MP %(AdditionalOptions) Disabled ..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories) - WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebugDLL @@ -194,7 +194,7 @@ /Zl /MP %(AdditionalOptions) OnlyExplicitInline ..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories) - WIN32;USE_STANDARD_MALLOC;;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;;NDEBUG;_LIB;%(PreprocessorDefinitions) true false MultiThreadedDLL @@ -221,7 +221,7 @@ /Zl /MP %(AdditionalOptions) OnlyExplicitInline ..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories) - WIN32;USE_STANDARD_MALLOC;;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;;NDEBUG;_LIB;%(PreprocessorDefinitions) true false MultiThreadedDLL @@ -245,7 +245,7 @@ /Zl /MP %(AdditionalOptions) Disabled ..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories) - WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebugDLL @@ -272,7 +272,7 @@ /Zl /MP %(AdditionalOptions) Disabled ..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories) - WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebugDLL