Merge remote branch 'origin/master' into 330

Conflicts:
	src/game/DBCfmt.h
	src/game/GossipDef.cpp
	src/game/Mail.cpp
This commit is contained in:
tomrus88 2009-11-04 02:37:09 +03:00
commit 2732c33465
48 changed files with 1023 additions and 786 deletions

View file

@ -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',

View file

@ -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;

View file

@ -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';

View file

@ -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;

View file

@ -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

View file

@ -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));
}
}

View file

@ -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));
}

View file

@ -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
{

View file

@ -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

View file

@ -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));
}
}

View file

@ -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 },

View file

@ -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);

View file

@ -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<Creature>(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<Creature>(GetGUID(), (Creature*)NULL);
Unit::RemoveFromWorld();
@ -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))

View file

@ -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

View file

@ -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

View file

@ -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";

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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,18 +879,22 @@ 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))
{
uint32 GuildId = GetPlayer()->GetGuildId();
if (!GuildId)
return;
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,10 +913,17 @@ void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data )
if (GetPlayer()->GetMoney() < money)
return;
if(uint32 GuildId = GetPlayer()->GetGuildId())
{
if(Guild *pGuild = objmgr.GetGuildById(GuildId))
{
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);
@ -935,8 +946,6 @@ void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data )
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()<money) // not enough money in bank
return;
@ -1005,6 +1017,21 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
uint32 SplitedAmount = 0;
recv_data >> 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,10 +1083,6 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return;
if(uint32 GuildId = GetPlayer()->GetGuildId())
{
if(Guild *pGuild = objmgr.GetGuildById(GuildId))
{
// Bank <-> Bank
if (BankToBank)
{
@ -1078,8 +1105,6 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
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,16 +1169,21 @@ 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))
{
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,8 +1192,21 @@ void WorldSession::HandleGuildBankLogQuery( WorldPacket & recv_data )
uint8 TabId;
recv_data >> TabId;
if(uint32 GuildId = GetPlayer()->GetGuildId())
if(Guild *pGuild = objmgr.GetGuildById(GuildId))
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);
}
@ -1179,8 +1217,17 @@ void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data)
uint8 TabId;
recv_data >> TabId;
if(uint32 GuildId = GetPlayer()->GetGuildId())
if(Guild *pGuild = objmgr.GetGuildById(GuildId))
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);
}
@ -1193,8 +1240,17 @@ void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data)
recv_data >> TabId;
recv_data >> Text;
if(uint32 GuildId = GetPlayer()->GetGuildId())
if(Guild *pGuild = objmgr.GetGuildById(GuildId))
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);
}

View file

@ -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());

View file

@ -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());

View file

@ -40,10 +40,10 @@ LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenc
LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true);
LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true);
LootStore LootTemplates_Item( "item_loot_template", "item entry", true);
LootStore LootTemplates_Mail( "mail_loot_template", "mail template id", false);
LootStore LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true);
LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true);
LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true);
LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id (with mail template)",false);
LootStore LootTemplates_Reference( "reference_loot_template", "reference id", false);
LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true);
LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false);
@ -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)

View file

@ -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();

View file

@ -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)
{
uint8 item_slot;
uint64 item_guid;
recv_data >> item_slot;
recv_data >> item_guid;
mi.AddItem(GUID_LOPART(item_guid), item_slot);
}
recv_data.read_skip<uint8>(); // item slot in mail, not used
recv_data >> itemGUIDs[i];
}
recv_data >> money >> COD; // money and cod
recv_data >> unk3; // const 0
recv_data >> unk4; // const 0
items_count = mi.size(); // this is the real size after the duplicates have been removed
// packet read complete, now do check
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
if (receiver.empty())
return;
@ -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,95 +154,89 @@ 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)
{
for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
{
MailItem& mailItem = mailItemIter->second;
Item* items[MAX_MAIL_ITEMS];
if(!mailItem.item_guidlow)
for(uint8 i = 0; i < items_count; ++i)
{
if (!itemGUIDs[i])
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM));
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(!mailItem.item)
if(!item)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
if(!mailItem.item->CanBeTraded(true))
if (!item->CanBeTraded(true))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if(mailItem.item->IsBoundAccountWide() && mailItem.item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
return;
}
if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION))
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
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;
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;
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
@ -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<uint32>(); // 3.3.0
recv_data.read_skip<uint32>(); // 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<uint64>(); // original sender GUID for return to, not used
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
recv_data >> mailId;
Player *pl = _player;
Mail *m = pl->GetMail(mailId);
if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
@ -362,7 +344,12 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
CharacterDatabase.CommitTransaction();
pl->RemoveMail(mailId);
MailItemsInfo mi;
// send back only to players and simple drop for other cases
if (m->messageType == MAIL_NORMAL)
{
MailDraft draft(m->subject, m->itemTextId);
if (m->mailTemplateId)
draft = MailDraft(m->mailTemplateId,false); // items already included
if(m->HasItems())
{
@ -370,7 +357,7 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
{
Item *item = pl->GetMItem(itr2->item_guid);
if(item)
mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
draft.AddItem(item);
else
{
//WTF?
@ -380,59 +367,13 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
}
}
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<uint32>(); // 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 (!m_items.empty())
deleteIncludedItems();
}
else if (mi)
mi->deleteIncludedItems();
}
else if (mi)
mi->deleteIncludedItems();
CharacterDatabase.BeginTransaction();
CharacterDatabase.escape_string(subject);
CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) "
"VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" UI64FMTD "','" UI64FMTD "', '%u', '%u', '%d')",
mailId, messageType, stationery, mailTemplateId, sender_guidlow_or_entry, receiver_guidlow, subject.c_str(), itemTextId, (mi && !mi->empty() ? 1 : 0), (uint64)expire_time, (uint64)deliver_time, money, COD, checked);
if (mi)
{
for(MailItemMap::const_iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter)
{
MailItem const& mailItem = mailItemIter->second;
CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, mailItem.item_guidlow, mailItem.item_template, receiver_guidlow);
}
}
CharacterDatabase.CommitTransaction();
else if (!m_items.empty())
deleteIncludedItems();
}

View file

@ -21,20 +21,12 @@
#include "Common.h"
#include <map>
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<uint32, Item*> MailItemMap;
public: // Constructors
explicit MailDraft(uint16 mailTemplateId, bool need_items = true)
: m_mailTemplateId(mailTemplateId), m_mailTemplateItemsNeed(need_items), m_bodyId(0), m_money(0), m_COD(0)
{}
MailDraft(std::string subject, uint32 itemTextId = 0)
: m_mailTemplateId(0), m_mailTemplateItemsNeed(false), m_subject(subject), m_bodyId(itemTextId), m_money(0), m_COD(0) {}
public: // Accessors
uint16 GetMailTemplateId() const { return m_mailTemplateId; }
std::string const& GetSubject() const { return m_subject; }
uint32 GetBodyId() const { return m_bodyId; }
uint32 GetMoney() const { return m_money; }
uint32 GetCOD() const { return m_COD; }
public: // modifiers
MailDraft& AddItem(Item* item);
MailDraft& AddMoney(uint32 money) { m_money = money; return *this; }
MailDraft& AddCOD(uint32 COD) { m_COD = COD; return *this; }
public: // finishers
void SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid);
void SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0);
private:
void deleteIncludedItems(bool inDB = false);
void prepareItems(Player* receiver); // called from SendMailTo for generate mailTemplateBase items
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<uint32, MailItem> 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<MailItemInfo>::iterator itr = items.begin(); itr != items.end(); ++itr)
@ -186,4 +173,5 @@ struct Mail
bool HasItems() const { return !items.empty(); }
};
#endif

View file

@ -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");

View file

@ -104,17 +104,6 @@ template<> void addUnitState(Creature *obj, CellPair const& cell_pair)
obj->SetCurrentCell(cell);
}
template<class T> bool alreadyLoaded(Map* /*map*/, uint32 /*guid*/)
{
// Non creature objects not walk by grids
return false;
}
template<> bool alreadyLoaded<Creature>(Map* map, uint32 guid)
{
return map->GetObjectsStore().find<Creature>(guid,(Creature*)NULL);
}
template <class T>
void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> &m, uint32 &count, Map* map)
{
@ -124,13 +113,6 @@ void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> &
{
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<T>(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))

View file

@ -2163,6 +2163,7 @@ void ObjectMgr::LoadPetLevelInfo()
if (!result)
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString(">> Loaded %u level pet stats definitions", count);
@ -3278,6 +3279,9 @@ void ObjectMgr::LoadQuests()
delete result;
// Post processing
std::map<uint32,uint32> usedMailTemplates;
for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
{
Quest * qinfo = iter->second;
@ -3775,6 +3779,16 @@ void ObjectMgr::LoadQuests()
qinfo->RewMailTemplateId = 0; // no mail will send to player
qinfo->RewMailDelaySecs = 0; // no mail will send to player
}
else if (usedMailTemplates.find(qinfo->RewMailTemplateId) != usedMailTemplates.end())
{
std::map<uint32,uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewMailTemplateId);
sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId,used_mt_itr->second);
qinfo->RewMailTemplateId = 0; // no mail will send to player
qinfo->RewMailDelaySecs = 0; // no mail will send to player
}
else
usedMailTemplates[qinfo->RewMailTemplateId] = qinfo->GetQuestId();
}
if (qinfo->NextQuestInChain)
@ -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

View file

@ -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<MailLevelReward> MailLevelRewardList;
typedef UNORDERED_MAP<uint8,MailLevelRewardList> 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<int32, uint32> ExclusiveQuestGroups;
ExclusiveQuestGroups mExclusiveQuestGroups;
MailLevelReward const* GetMailLevelReward(uint32 level,uint32 raceMask)
{
MailLevelRewardMap::const_iterator map_itr = m_mailLevelRewardMap.find(level);
if (map_itr == m_mailLevelRewardMap.end())
return NULL;
for(MailLevelRewardList::const_iterator set_itr = map_itr->second.begin(); set_itr != map_itr->second.end(); ++set_itr)
if (set_itr->raceMask & raceMask)
return &*set_itr;
return NULL;
}
WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
{
WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
@ -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<uint32,PetLevelInfo*> PetLevelInfoMap;
// PetLevelInfoMap[creature_id][level]
PetLevelInfoMap petInfo; // [creature_id][level]

View file

@ -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));
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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));

View file

@ -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;

View file

@ -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);
}
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);
}
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);
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);
if (attackType != RANGED_ATTACK)
crit -= pVictim->GetMeleeCritChanceReduction();
else
crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE);
}
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);
}

View file

@ -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;

View file

@ -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()

View file

@ -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
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 );

View file

@ -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

View file

@ -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

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "8745"
#define REVISION_NR "8770"
#endif // __REVISION_NR_H__

View file

@ -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__

View file

@ -143,7 +143,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -170,7 +170,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -194,7 +194,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;USE_STANDARD_MALLOC;;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@ -221,7 +221,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;USE_STANDARD_MALLOC;;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@ -245,7 +245,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -272,7 +272,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;USE_STANDARD_MALLOC;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;;_DEBUG;MANGOS_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>