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, `version` varchar(120) default NULL,
`creature_ai_version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL,
`cache_id` int(10) default '0', `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'; ) 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 */; /*!40000 ALTER TABLE `locales_quest` ENABLE KEYS */;
UNLOCK TABLES; 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` -- Table structure for table `mangos_string`
-- --
@ -13561,33 +13610,6 @@ LOCK TABLES `quest_start_scripts` WRITE;
/*!40000 ALTER TABLE `quest_start_scripts` ENABLE KEYS */; /*!40000 ALTER TABLE `quest_start_scripts` ENABLE KEYS */;
UNLOCK TABLES; 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` -- Table structure for table `quest_template`
-- --
@ -13599,7 +13621,7 @@ CREATE TABLE `quest_template` (
`ZoneOrSort` smallint(6) NOT NULL default '0', `ZoneOrSort` smallint(6) NOT NULL default '0',
`SkillOrClass` smallint(6) NOT NULL default '0', `SkillOrClass` smallint(6) NOT NULL default '0',
`MinLevel` tinyint(3) unsigned 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', `Type` smallint(5) unsigned NOT NULL default '0',
`RequiredRaces` smallint(5) unsigned NOT NULL default '0', `RequiredRaces` smallint(5) unsigned NOT NULL default '0',
`RequiredSkillValue` 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 \ 8726_01_mangos_spell_proc_event.sql \
8728_01_realmd_account.sql \ 8728_01_realmd_account.sql \
8731_01_mangos_creature_template.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 README
## Additional files to include when running 'make dist' ## Additional files to include when running 'make dist'
@ -270,4 +273,7 @@ EXTRA_DIST = \
8726_01_mangos_spell_proc_event.sql \ 8726_01_mangos_spell_proc_event.sql \
8728_01_realmd_account.sql \ 8728_01_realmd_account.sql \
8731_01_mangos_creature_template.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 README

View file

@ -1706,16 +1706,6 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
{ {
Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL; 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(); int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
// subject and text // subject and text
@ -1734,7 +1724,18 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
uint32 itemTextId = objmgr.CreateItemText( text ); 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 // ELO system
if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6)
if (enemy_rating < 1300) if (enemy_rating < 1500)
enemy_rating = 1300; enemy_rating = 1500;
return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f)); 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) if (oldBidder)
oldBidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); 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; std::ostringstream msgAuctionCancelledSubject;
msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER; 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; std::ostringstream msgAuctionCanceledOwner;
msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED; 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 // 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 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.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
MailItemsInfo mi;
mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
if (bidder) if (bidder)
bidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template); bidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template);
else else
RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
// will delete item or place to receiver mail list // 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 // receiver not exist
else else
@ -187,7 +186,8 @@ void AuctionHouseMgr::SendAuctionSalePendingMail( AuctionEntry * auction )
uint32 itemTextId = objmgr.CreateItemText( msgAuctionSalePendingBody.str() ); 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 ); 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 else
RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! 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 // 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 // owner not found
else else

View file

@ -933,10 +933,6 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
// save new item before send // save new item before send
markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted 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 // subject: item name
std::string subject = markProto->Name1; std::string subject = markProto->Name1;
int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex(); 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()); snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
uint32 itemTextId = objmgr.CreateItemText( textBuf ); 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_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL },
{ "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL }, { "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL },
{ "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", 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 }, { "mangos_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMangosStringCommand, "", NULL },
{ "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL }, { "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL },
{ "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", 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}, { "points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPointsOfInterestCommand, "",NULL},
{ "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL }, { "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL },
{ "quest_end_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", 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_start_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL },
{ "quest_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL }, { "quest_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL },
{ "reference_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL }, { "reference_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL },

View file

@ -351,13 +351,14 @@ class ChatHandler
bool HandleReloadLootTemplatesFishingCommand(const char* args); bool HandleReloadLootTemplatesFishingCommand(const char* args);
bool HandleReloadLootTemplatesGameobjectCommand(const char* args); bool HandleReloadLootTemplatesGameobjectCommand(const char* args);
bool HandleReloadLootTemplatesItemCommand(const char* args); bool HandleReloadLootTemplatesItemCommand(const char* args);
bool HandleReloadLootTemplatesMailCommand(const char* args);
bool HandleReloadLootTemplatesMillingCommand(const char* args); bool HandleReloadLootTemplatesMillingCommand(const char* args);
bool HandleReloadLootTemplatesPickpocketingCommand(const char* args); bool HandleReloadLootTemplatesPickpocketingCommand(const char* args);
bool HandleReloadLootTemplatesProspectingCommand(const char* args); bool HandleReloadLootTemplatesProspectingCommand(const char* args);
bool HandleReloadLootTemplatesReferenceCommand(const char* args); bool HandleReloadLootTemplatesReferenceCommand(const char* args);
bool HandleReloadLootTemplatesQuestMailCommand(const char* args);
bool HandleReloadLootTemplatesSkinningCommand(const char* args); bool HandleReloadLootTemplatesSkinningCommand(const char* args);
bool HandleReloadLootTemplatesSpellCommand(const char* args); bool HandleReloadLootTemplatesSpellCommand(const char* args);
bool HandleReloadMailLevelRewardCommand(const char* args);
bool HandleReloadMangosStringCommand(const char* args); bool HandleReloadMangosStringCommand(const char* args);
bool HandleReloadNpcGossipCommand(const char* args); bool HandleReloadNpcGossipCommand(const char* args);
bool HandleReloadNpcOptionCommand(const char* args); bool HandleReloadNpcOptionCommand(const char* args);

View file

@ -141,7 +141,7 @@ Creature::~Creature()
void Creature::AddToWorld() void Creature::AddToWorld()
{ {
///- Register the creature for guid lookup ///- Register the creature for guid lookup
if(!IsInWorld()) if(!IsInWorld() && GetGUIDHigh()==HIGHGUID_UNIT)
GetMap()->GetObjectsStore().insert<Creature>(GetGUID(), (Creature*)this); GetMap()->GetObjectsStore().insert<Creature>(GetGUID(), (Creature*)this);
Unit::AddToWorld(); Unit::AddToWorld();
@ -150,7 +150,7 @@ void Creature::AddToWorld()
void Creature::RemoveFromWorld() void Creature::RemoveFromWorld()
{ {
///- Remove the creature from the accessor ///- Remove the creature from the accessor
if(IsInWorld()) if(IsInWorld() && GetGUIDHigh()==HIGHGUID_UNIT)
GetMap()->GetObjectsStore().erase<Creature>(GetGUID(), (Creature*)NULL); GetMap()->GetObjectsStore().erase<Creature>(GetGUID(), (Creature*)NULL);
Unit::RemoveFromWorld(); Unit::RemoveFromWorld();
@ -574,7 +574,7 @@ bool Creature::AIM_Initialize()
return true; return true;
} }
bool Creature::Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data) bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data)
{ {
ASSERT(map); ASSERT(map);
SetMap(map); SetMap(map);
@ -1353,7 +1353,17 @@ bool Creature::LoadFromDB(uint32 guid, Map *map)
} }
m_DBTableGuid = guid; 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; uint16 team = 0;
if(!Create(guid,map,data->phaseMask,data->id,team,data)) if(!Create(guid,map,data->phaseMask,data->id,team,data))

View file

@ -470,7 +470,7 @@ class MANGOS_DLL_SPEC Creature : public Unit
void AddToWorld(); void AddToWorld();
void RemoveFromWorld(); void RemoveFromWorld();
bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data = NULL); bool Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data = NULL);
bool LoadCreaturesAddon(bool reload = false); bool LoadCreaturesAddon(bool reload = false);
void SelectLevel(const CreatureInfo *cinfo); void SelectLevel(const CreatureInfo *cinfo);
void LoadEquipment(uint32 equip_entry, bool force=false); void LoadEquipment(uint32 equip_entry, bool force=false);

View file

@ -33,10 +33,11 @@ uint32 GetTalentSpellCost(uint32 spellId);
TalentSpellPos const* GetTalentSpellPos(uint32 spellId); TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found 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); 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); uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId);
enum ContentLevels enum ContentLevels

View file

@ -1069,7 +1069,7 @@ struct MailTemplateEntry
uint32 ID; // 0 uint32 ID; // 0
//char* subject[16]; // 1-16 //char* subject[16]; // 1-16
// 17 name flags, unused // 17 name flags, unused
//char* content[16]; // 18-33 char* content[16]; // 18-33
}; };
struct MapEntry struct MapEntry

View file

@ -71,7 +71,7 @@ const char ItemRandomPropertiesfmt[]="nxiiiiissssssssssssssssx";
const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiiiiiiiii"; const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiiiiiiiii";
const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii"; const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii";
const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx";
const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx";
const char MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxixx"; const char MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxixx";
const char MapDifficultyEntryfmt[]="diixxxxxxxxxxxxxxxxxiix"; const char MapDifficultyEntryfmt[]="diixxxxxxxxxxxxxxxxxiix";
const char MovieEntryfmt[]="nxx"; const char MovieEntryfmt[]="nxx";

View file

@ -151,7 +151,7 @@ void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
data << uint32(questID); data << uint32(questID);
data << uint32(qItem.m_qIcon); data << uint32(qItem.m_qIcon);
data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest)); data << int32(pQuest->GetQuestLevel());
std::string Title = pQuest->GetTitle(); std::string Title = pQuest->GetTitle();
int loc_idx = pSession->GetSessionDbLocaleIndex(); int loc_idx = pSession->GetSessionDbLocaleIndex();
@ -400,7 +400,7 @@ void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Titl
data << uint32(questID); data << uint32(questID);
data << uint32(qmi.m_qIcon); data << uint32(qmi.m_qIcon);
data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest)); data << int32(pQuest->GetQuestLevel());
data << title; data << title;
} }
pSession->SendPacket( &data ); pSession->SendPacket( &data );
@ -560,7 +560,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
data << uint32(pQuest->GetQuestId()); // quest id data << uint32(pQuest->GetQuestId()); // quest id
data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details) 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(0); // min level
data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log 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 // Bank content related
void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId) void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId)
{ {
WorldPacket data(SMSG_GUILD_BANK_LIST,1200); GuildBankTab const* tab = m_TabListMap[TabId];
GuildBankTab const* tab = GetBankTab(TabId);
if (!tab)
return;
if (!IsMemberHaveRights(session->GetPlayer()->GetGUIDLow(),TabId,GUILD_BANK_RIGHT_VIEW_TAB)) if (!IsMemberHaveRights(session->GetPlayer()->GetGUIDLow(),TabId,GUILD_BANK_RIGHT_VIEW_TAB))
return; return;
WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
data << uint64(GetGuildBankMoney()); data << uint64(GetGuildBankMoney());
data << uint8(TabId); data << uint8(TabId);
// remaining slots for today // remaining slots for today
@ -992,9 +990,7 @@ void Guild::DisplayGuildBankMoneyUpdate()
void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2) void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2)
{ {
GuildBankTab const* tab = GetBankTab(TabId); GuildBankTab const* tab = m_TabListMap[TabId];
if (!tab)
return;
WorldPacket data(SMSG_GUILD_BANK_LIST,1200); 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) void Guild::DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots)
{ {
GuildBankTab const* tab = GetBankTab(TabId); GuildBankTab const* tab = m_TabListMap[TabId];
if (!tab)
return;
WorldPacket data(SMSG_GUILD_BANK_LIST,1200); 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) 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) if (m_TabListMap[TabId]->Name == Name && m_TabListMap[TabId]->Icon == Icon)
return; return;
@ -1993,12 +1979,7 @@ void Guild::SetGuildBankTabText(uint8 TabId, std::string text)
void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId) void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId)
{ {
if (TabId > GUILD_BANK_MAX_TABS) GuildBankTab const* tab = m_TabListMap[TabId];
return;
GuildBankTab const *tab = GetBankTab(TabId);
if (!tab)
return;
WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1); WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1);
data << uint8(TabId); data << uint8(TabId);

View file

@ -400,7 +400,6 @@ class Guild
void SetGuildBankTabText(uint8 TabId, std::string text); void SetGuildBankTabText(uint8 TabId, std::string text);
void SendGuildBankTabText(WorldSession *session, uint8 TabId); void SendGuildBankTabText(WorldSession *session, uint8 TabId);
void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon); 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; } uint8 GetPurchasedTabs() const { return m_PurchasedTabs; }
uint32 GetBankRights(uint32 rankId, uint8 TabId) const; uint32 GetBankRights(uint32 rankId, uint8 TabId) const;
bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const; bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const;
@ -408,6 +407,7 @@ class Guild
// Load/unload // Load/unload
void LoadGuildBankFromDB(); void LoadGuildBankFromDB();
void UnloadGuildBank(); void UnloadGuildBank();
bool IsGuildBankLoaded() const { return m_GuildBankLoaded; }
void IncOnlineMemberCount() { ++m_OnlineMembers; } void IncOnlineMemberCount() { ++m_OnlineMembers; }
// Money deposit/withdraw // Money deposit/withdraw
void SendMoneyInfo(WorldSession *session, uint32 LowGuid); void SendMoneyInfo(WorldSession *session, uint32 LowGuid);

View file

@ -859,7 +859,7 @@ void WorldSession::HandleGuildBankerActivate( WorldPacket & recv_data )
{ {
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) if(Guild *pGuild = objmgr.GetGuildById(GuildId))
{ {
pGuild->DisplayGuildBankTabsInfo(this); pGuild->DisplayGuildBankTabsInfo(this); // this also will load guild bank if not yet
return; return;
} }
} }
@ -879,17 +879,21 @@ void WorldSession::HandleGuildBankQueryTab( WorldPacket & recv_data )
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return; return;
if(uint32 GuildId = GetPlayer()->GetGuildId()) uint32 GuildId = GetPlayer()->GetGuildId();
{ if (!GuildId)
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) 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); 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 ) void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data )
@ -909,33 +913,38 @@ void WorldSession::HandleGuildBankDepositMoney( WorldPacket & recv_data )
if (GetPlayer()->GetMoney() < money) if (GetPlayer()->GetMoney() < money)
return; return;
if(uint32 GuildId = GetPlayer()->GetGuildId()) uint32 GuildId = GetPlayer()->GetGuildId();
if (!GuildId)
return;
Guild *pGuild = objmgr.GetGuildById(GuildId);
if (!pGuild)
return;
if (!pGuild->IsGuildBankLoaded() || !pGuild->GetPurchasedTabs())
return;
CharacterDatabase.BeginTransaction();
pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money);
GetPlayer()->ModifyMoney(-int(money));
GetPlayer()->SaveGoldToDB();
CharacterDatabase.CommitTransaction();
// logging money
if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{ {
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)",
{ _player->GetName(),_player->GetSession()->GetAccountId(),money,GuildId);
CharacterDatabase.BeginTransaction();
pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money);
GetPlayer()->ModifyMoney(-int(money));
GetPlayer()->SaveGoldToDB();
CharacterDatabase.CommitTransaction();
// logging money
if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)",
_player->GetName(),_player->GetSession()->GetAccountId(),money,GuildId);
}
// log
pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money);
pGuild->DisplayGuildBankTabsInfo(this);
pGuild->DisplayGuildBankContent(this, 0);
pGuild->DisplayGuildBankMoneyUpdate();
}
} }
// log
pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money);
pGuild->DisplayGuildBankTabsInfo(this);
pGuild->DisplayGuildBankContent(this, 0);
pGuild->DisplayGuildBankMoneyUpdate();
} }
void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data ) void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data )
@ -960,6 +969,9 @@ void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data )
if(!pGuild) if(!pGuild)
return; return;
if (!pGuild->IsGuildBankLoaded() || !pGuild->GetPurchasedTabs())
return;
if (pGuild->GetGuildBankMoney()<money) // not enough money in bank if (pGuild->GetGuildBankMoney()<money) // not enough money in bank
return; return;
@ -1005,6 +1017,21 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
uint32 SplitedAmount = 0; uint32 SplitedAmount = 0;
recv_data >> GoGuid >> BankToBank; 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) if (BankToBank)
{ {
recv_data >> BankTabDst; recv_data >> BankTabDst;
@ -1016,7 +1043,10 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
recv_data >> unk2; // always 0 recv_data >> unk2; // always 0
recv_data >> SplitedAmount; 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 recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet
return; return;
@ -1042,7 +1072,8 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
recv_data >> SplitedAmount; 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 recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet
return; return;
@ -1052,33 +1083,27 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return; return;
if(uint32 GuildId = GetPlayer()->GetGuildId()) // Bank <-> Bank
if (BankToBank)
{ {
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) pGuild->SwapItems(_player, BankTab, BankTabSlot, BankTabDst, BankTabSlotDst, SplitedAmount);
{ return;
// Bank <-> Bank
if (BankToBank)
{
pGuild->SwapItems(_player, BankTab, BankTabSlot, BankTabDst, BankTabSlotDst, SplitedAmount);
return;
}
// Player <-> Bank
// allow work with inventory only
if(!Player::IsInventoryPos(PlayerBag, PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) )
{
_player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL );
return;
}
// BankToChar swap or char to bank remaining
if (ToChar) // Bank -> Char cases
pGuild->MoveFromBankToChar(_player, BankTab, BankTabSlot, PlayerBag, PlayerSlot, SplitedAmount);
else // Char -> Bank cases
pGuild->MoveFromCharToBank(_player, PlayerBag, PlayerSlot, BankTab, BankTabSlot, SplitedAmount);
}
} }
// Player <-> Bank
// allow work with inventory only
if(!Player::IsInventoryPos(PlayerBag, PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) )
{
_player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL );
return;
}
// BankToChar swap or char to bank remaining
if (ToChar) // Bank -> Char cases
pGuild->MoveFromBankToChar(_player, BankTab, BankTabSlot, PlayerBag, PlayerSlot, SplitedAmount);
else // Char -> Bank cases
pGuild->MoveFromCharToBank(_player, PlayerBag, PlayerSlot, BankTab, BankTabSlot, SplitedAmount);
} }
void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data )
@ -1095,26 +1120,21 @@ void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data )
return; return;
uint32 GuildId = GetPlayer()->GetGuildId(); uint32 GuildId = GetPlayer()->GetGuildId();
if (GuildId==0) if (!GuildId)
return; return;
Guild *pGuild = objmgr.GetGuildById(GuildId); Guild *pGuild = objmgr.GetGuildById(GuildId);
if(!pGuild) if(!pGuild)
return; 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; uint32 TabCost = GetGuildBankTabPrice(TabId) * GOLD;
if (!TabCost) if (!TabCost)
return; 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 if (GetPlayer()->GetMoney() < TabCost) // Should not happen, this is checked by client
return; return;
@ -1149,15 +1169,20 @@ void WorldSession::HandleGuildBankUpdateTab( WorldPacket & recv_data )
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return; return;
if(uint32 GuildId = GetPlayer()->GetGuildId()) uint32 GuildId = GetPlayer()->GetGuildId();
{ if (!GuildId)
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) return;
{
pGuild->SetGuildBankTabInfo(TabId, Name, IconIndex); Guild *pGuild = objmgr.GetGuildById(GuildId);
pGuild->DisplayGuildBankTabsInfo(this); if (!pGuild)
pGuild->DisplayGuildBankContent(this, TabId); 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 ) void WorldSession::HandleGuildBankLogQuery( WorldPacket & recv_data )
@ -1167,9 +1192,22 @@ void WorldSession::HandleGuildBankLogQuery( WorldPacket & recv_data )
uint8 TabId; uint8 TabId;
recv_data >> TabId; recv_data >> TabId;
if(uint32 GuildId = GetPlayer()->GetGuildId()) uint32 GuildId = GetPlayer()->GetGuildId();
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) if (!GuildId)
pGuild->DisplayGuildBankLogs(this, TabId); return;
Guild *pGuild = objmgr.GetGuildById(GuildId);
if (!pGuild)
return;
if (!pGuild->IsGuildBankLoaded())
return;
// GUILD_BANK_MAX_TABS send by client for money log
if (TabId >= pGuild->GetPurchasedTabs() && TabId != GUILD_BANK_MAX_TABS)
return;
pGuild->DisplayGuildBankLogs(this, TabId);
} }
void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data) void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data)
@ -1179,9 +1217,18 @@ void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data)
uint8 TabId; uint8 TabId;
recv_data >> TabId; recv_data >> TabId;
if(uint32 GuildId = GetPlayer()->GetGuildId()) uint32 GuildId = GetPlayer()->GetGuildId();
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) if (!GuildId)
pGuild->SendGuildBankTabText(this, TabId); return;
Guild *pGuild = objmgr.GetGuildById(GuildId);
if (!pGuild)
return;
if (!pGuild->IsGuildBankLoaded() || TabId >= pGuild->GetPurchasedTabs())
return;
pGuild->SendGuildBankTabText(this, TabId);
} }
void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data) void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data)
@ -1193,9 +1240,18 @@ void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data)
recv_data >> TabId; recv_data >> TabId;
recv_data >> Text; recv_data >> Text;
if(uint32 GuildId = GetPlayer()->GetGuildId()) uint32 GuildId = GetPlayer()->GetGuildId();
if(Guild *pGuild = objmgr.GetGuildById(GuildId)) if (!GuildId)
pGuild->SetGuildBankTabText(TabId, Text); return;
Guild *pGuild = objmgr.GetGuildById(GuildId);
if (!pGuild)
return;
if (!pGuild->IsGuildBankLoaded() || TabId >= pGuild->GetPurchasedTabs())
return;
pGuild->SetGuildBankTabText(TabId, Text);
} }
void WorldSession::SendSaveGuildEmblem( uint32 msg ) void WorldSession::SendSaveGuildEmblem( uint32 msg )

View file

@ -2003,13 +2003,12 @@ bool ChatHandler::HandleSendMailCommand(const char* args)
std::string text = msgText; std::string text = msgText;
// from console show not existed sender // 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; 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); std::string nameLink = playerLink(target_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());

View file

@ -67,6 +67,7 @@ bool ChatHandler::HandleReloadAllCommand(const char*)
HandleReloadAllItemCommand(""); HandleReloadAllItemCommand("");
HandleReloadAllLocalesCommand(""); HandleReloadAllLocalesCommand("");
HandleReloadMailLevelRewardCommand("");
HandleReloadCommandCommand(""); HandleReloadCommandCommand("");
HandleReloadReservedNameCommand(""); HandleReloadReservedNameCommand("");
HandleReloadMangosStringCommand(""); HandleReloadMangosStringCommand("");
@ -360,12 +361,12 @@ bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
return true; return true;
} }
bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*) bool ChatHandler::HandleReloadLootTemplatesMailCommand(const char*)
{ {
sLog.outString( "Re-Loading Loot Tables... (`quest_mail_loot_template`)" ); sLog.outString( "Re-Loading Loot Tables... (`mail_loot_template`)" );
LoadLootTemplates_QuestMail(); LoadLootTemplates_Mail();
LootTemplates_QuestMail.CheckLootRefs(); LootTemplates_Mail.CheckLootRefs();
SendGlobalSysMessage("DB table `quest_mail_loot_template` reloaded."); SendGlobalSysMessage("DB table `mail_loot_template` reloaded.");
return true; return true;
} }
@ -822,6 +823,14 @@ bool ChatHandler::HandleReloadLocalesQuestCommand(const char* /*arg*/)
return true; 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) bool ChatHandler::HandleLoadScriptsCommand(const char* args)
{ {
if(!LoadScriptingModule(args)) return true; if(!LoadScriptingModule(args)) return true;
@ -6216,25 +6225,23 @@ bool ChatHandler::HandleSendItemsCommand(const char* args)
} }
// from console show not existed sender // 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; uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
// fill mail // fill mail
MailItemsInfo mi; // item list preparing MailDraft draft(subject, itemTextId);
for(ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr) 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)) 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 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); std::string nameLink = playerLink(receiver_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
@ -6278,13 +6285,13 @@ bool ChatHandler::HandleSendMoneyCommand(const char* args)
std::string text = msgText; std::string text = msgText;
// from console show not existed sender // 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; 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); std::string nameLink = playerLink(receiver_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
@ -6387,4 +6394,4 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args)
ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full, GetNameLink().c_str()); ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full, GetNameLink().c_str());
return true; return true;
} }

View file

@ -35,17 +35,17 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT
}; };
LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true); LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true);
LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true); LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true);
LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true); LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true);
LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true); LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true);
LootStore LootTemplates_Item( "item_loot_template", "item entry", true); LootStore LootTemplates_Item( "item_loot_template", "item entry", true);
LootStore LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true); LootStore LootTemplates_Mail( "mail_loot_template", "mail template id", false);
LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true); LootStore LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true);
LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true); LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true);
LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id (with mail template)",false); LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true);
LootStore LootTemplates_Reference( "reference_loot_template", "reference id", false); LootStore LootTemplates_Reference( "reference_loot_template", "reference id", false);
LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true); LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true);
LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false); LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false);
class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed) class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
@ -1256,28 +1256,19 @@ void LoadLootTemplates_Prospecting()
LootTemplates_Prospecting.ReportUnusedIds(ids_set); LootTemplates_Prospecting.ReportUnusedIds(ids_set);
} }
void LoadLootTemplates_QuestMail() void LoadLootTemplates_Mail()
{ {
LootIdSet ids_set; LootIdSet ids_set;
LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set); LootTemplates_Mail.LoadAndCollectLootIds(ids_set);
// remove real entries and check existence loot // remove real entries and check existence loot
ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates(); for(uint32 i = 1; i < sMailTemplateStore.GetNumRows(); ++i )
for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr ) if(sMailTemplateStore.LookupEntry(i))
{ if(ids_set.count(i))
if(!itr->second->GetRewMailTemplateId()) ids_set.erase(i);
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);
*/
}
// output error for any still listed (not referenced from appropriate table) ids // 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() void LoadLootTemplates_Skinning()
@ -1354,7 +1345,7 @@ void LoadLootTemplates_Reference()
LootTemplates_Skinning.CheckLootRefs(&ids_set); LootTemplates_Skinning.CheckLootRefs(&ids_set);
LootTemplates_Disenchant.CheckLootRefs(&ids_set); LootTemplates_Disenchant.CheckLootRefs(&ids_set);
LootTemplates_Prospecting.CheckLootRefs(&ids_set); LootTemplates_Prospecting.CheckLootRefs(&ids_set);
LootTemplates_QuestMail.CheckLootRefs(&ids_set); LootTemplates_Mail.CheckLootRefs(&ids_set);
LootTemplates_Reference.CheckLootRefs(&ids_set); LootTemplates_Reference.CheckLootRefs(&ids_set);
// output error for any still listed ids (not referenced from any loot table) // 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_Fishing;
extern LootStore LootTemplates_Gameobject; extern LootStore LootTemplates_Gameobject;
extern LootStore LootTemplates_Item; extern LootStore LootTemplates_Item;
extern LootStore LootTemplates_Mail;
extern LootStore LootTemplates_Milling; extern LootStore LootTemplates_Milling;
extern LootStore LootTemplates_Pickpocketing; extern LootStore LootTemplates_Pickpocketing;
extern LootStore LootTemplates_Skinning; extern LootStore LootTemplates_Skinning;
extern LootStore LootTemplates_Disenchant; extern LootStore LootTemplates_Disenchant;
extern LootStore LootTemplates_Prospecting; extern LootStore LootTemplates_Prospecting;
extern LootStore LootTemplates_QuestMail;
extern LootStore LootTemplates_Spell; extern LootStore LootTemplates_Spell;
void LoadLootTemplates_Creature(); void LoadLootTemplates_Creature();
void LoadLootTemplates_Fishing(); void LoadLootTemplates_Fishing();
void LoadLootTemplates_Gameobject(); void LoadLootTemplates_Gameobject();
void LoadLootTemplates_Item(); void LoadLootTemplates_Item();
void LoadLootTemplates_Mail();
void LoadLootTemplates_Milling(); void LoadLootTemplates_Milling();
void LoadLootTemplates_Pickpocketing(); void LoadLootTemplates_Pickpocketing();
void LoadLootTemplates_Skinning(); void LoadLootTemplates_Skinning();
void LoadLootTemplates_Disenchant(); void LoadLootTemplates_Disenchant();
void LoadLootTemplates_Prospecting(); void LoadLootTemplates_Prospecting();
void LoadLootTemplates_QuestMail();
void LoadLootTemplates_Spell(); void LoadLootTemplates_Spell();
void LoadLootTemplates_Reference(); void LoadLootTemplates_Reference();
@ -352,12 +352,12 @@ inline void LoadLootTables()
LoadLootTemplates_Fishing(); LoadLootTemplates_Fishing();
LoadLootTemplates_Gameobject(); LoadLootTemplates_Gameobject();
LoadLootTemplates_Item(); LoadLootTemplates_Item();
LoadLootTemplates_Mail();
LoadLootTemplates_Milling(); LoadLootTemplates_Milling();
LoadLootTemplates_Pickpocketing(); LoadLootTemplates_Pickpocketing();
LoadLootTemplates_Skinning(); LoadLootTemplates_Skinning();
LoadLootTemplates_Disenchant(); LoadLootTemplates_Disenchant();
LoadLootTemplates_Prospecting(); LoadLootTemplates_Prospecting();
LoadLootTemplates_QuestMail();
LoadLootTemplates_Spell(); LoadLootTemplates_Spell();
LoadLootTemplates_Reference(); LoadLootTemplates_Reference();

View file

@ -29,6 +29,8 @@
#include "Language.h" #include "Language.h"
#include "DBCStores.h" #include "DBCStores.h"
#include "BattleGroundMgr.h" #include "BattleGroundMgr.h"
#include "Item.h"
#include "AuctionHouseMgr.h"
enum MailShowFlags enum MailShowFlags
{ {
@ -39,18 +41,6 @@ enum MailShowFlags
MAIL_SHOW_RETURN = 0x0010, 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 ) void WorldSession::HandleSendMail(WorldPacket & recv_data )
{ {
uint64 mailbox, unk3; uint64 mailbox, unk3;
@ -60,9 +50,6 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
recv_data >> mailbox; recv_data >> mailbox;
recv_data >> receiver; recv_data >> receiver;
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
recv_data >> subject; recv_data >> subject;
recv_data >> body; recv_data >> body;
@ -70,34 +57,32 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
recv_data >> unk1; // stationery? recv_data >> unk1; // stationery?
recv_data >> unk2; // 0x00000000 recv_data >> unk2; // 0x00000000
MailItemsInfo mi;
uint8 items_count; uint8 items_count;
recv_data >> items_count; // attached 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); GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
return; return;
} }
if(items_count) uint64 itemGUIDs[MAX_MAIL_ITEMS];
for(uint8 i = 0; i < items_count; ++i)
{ {
for(uint8 i = 0; i < items_count; ++i) recv_data.read_skip<uint8>(); // item slot in mail, not used
{ recv_data >> itemGUIDs[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 >> money >> COD; // money and cod recv_data >> money >> COD; // money and cod
recv_data >> unk3; // const 0 recv_data >> unk3; // const 0
recv_data >> unk4; // 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()) if (receiver.empty())
return; return;
@ -105,7 +90,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
Player* pl = _player; Player* pl = _player;
uint64 rc = 0; uint64 rc = 0;
if(normalizePlayerName(receiver)) if (normalizePlayerName(receiver))
rc = objmgr.GetPlayerGUIDByName(receiver); rc = objmgr.GetPlayerGUIDByName(receiver);
if (!rc) if (!rc)
@ -118,7 +103,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
if(pl->GetGUID() == rc) if (pl->GetGUID() == rc)
{ {
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
return; return;
@ -139,7 +124,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
uint32 rc_team = 0; uint32 rc_team = 0;
uint8 mails_count = 0; // do not allow to send to one player more than 100 mails uint8 mails_count = 0; // do not allow to send to one player more than 100 mails
if(receive) if (receive)
{ {
rc_team = receive->GetTeam(); rc_team = receive->GetTeam();
mails_count = receive->GetMailSize(); mails_count = receive->GetMailSize();
@ -147,8 +132,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
else else
{ {
rc_team = objmgr.GetPlayerTeamByGUID(rc); rc_team = objmgr.GetPlayerTeamByGUID(rc);
QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)); if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)))
if(result)
{ {
Field *fields = result->Fetch(); Field *fields = result->Fetch();
mails_count = fields[0].GetUInt32(); mails_count = fields[0].GetUInt32();
@ -170,102 +154,96 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
return; return;
} }
uint32 rc_account = 0; uint32 rc_account = receive
if(receive) ? receive->GetSession()->GetAccountId()
rc_account = receive->GetSession()->GetAccountId(); : objmgr.GetPlayerAccountIdByGUID(rc);
else
rc_account = objmgr.GetPlayerAccountIdByGUID(rc);
if (items_count) Item* items[MAX_MAIL_ITEMS];
for(uint8 i = 0; i < items_count; ++i)
{ {
for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) if (!itemGUIDs[i])
{ {
MailItem& mailItem = mailItemIter->second; pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
if(!mailItem.item_guidlow)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM));
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if(!mailItem.item)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
if(!mailItem.item->CanBeTraded(true))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if(mailItem.item->IsBoundAccountWide() && mailItem.item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
return;
}
if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
return;
}
} }
Item* item = pl->GetItemByGuid(itemGUIDs[i]);
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if(!item)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
if (!item->CanBeTraded(true))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
return;
}
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
return;
}
items[i] = item;
} }
pl->SendMailResult(0, MAIL_SEND, MAIL_OK); pl->SendMailResult(0, MAIL_SEND, MAIL_OK);
uint32 itemTextId = 0; uint32 itemTextId = !body.empty() ? objmgr.CreateItemText( body ) : 0;
if (!body.empty())
{
itemTextId = objmgr.CreateItemText( body );
}
pl->ModifyMoney( -int32(reqmoney) ); pl->ModifyMoney( -int32(reqmoney) );
pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost);
bool needItemDelay = false; bool needItemDelay = false;
if(items_count > 0 || money > 0) MailDraft draft(subject, itemTextId);
if (items_count > 0 || money > 0)
{ {
if (items_count > 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; Item* item = items[i];
if(!mailItem.item) if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
continue;
mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0;
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)", 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(); CharacterDatabase.BeginTransaction();
mailItem.item->DeleteFromInventoryDB(); // deletes item from character's inventory 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->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 // 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(); CharacterDatabase.CommitTransaction();
draft.AddItem(item);
} }
// if item send to character at another account, then apply item delivery delay // if item send to character at another account, then apply item delivery delay
needItemDelay = pl->GetSession()->GetAccountId() != rc_account; needItemDelay = pl->GetSession()->GetAccountId() != rc_account;
} }
if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{ {
sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)",
GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account);
@ -276,7 +254,10 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
// will delete item or place to receiver mail list // 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(); CharacterDatabase.BeginTransaction();
pl->SaveInventoryAndGoldToDB(); pl->SaveInventoryAndGoldToDB();
@ -314,7 +295,7 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data )
uint32 mailId; uint32 mailId;
recv_data >> mailbox; recv_data >> mailbox;
recv_data >> mailId; recv_data >> mailId;
recv_data.read_skip<uint32>(); // 3.3.0 recv_data.read_skip<uint32>(); // mailTemplateId
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return; return;
@ -341,11 +322,12 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
uint64 mailbox; uint64 mailbox;
uint32 mailId; uint32 mailId;
recv_data >> mailbox; 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)) if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return; return;
recv_data >> mailId;
Player *pl = _player; Player *pl = _player;
Mail *m = pl->GetMail(mailId); Mail *m = pl->GetMail(mailId);
if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
@ -362,77 +344,36 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
pl->RemoveMail(mailId); pl->RemoveMail(mailId);
MailItemsInfo mi; // send back only to players and simple drop for other cases
if (m->messageType == MAIL_NORMAL)
if(m->HasItems())
{ {
for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) MailDraft draft(m->subject, m->itemTextId);
if (m->mailTemplateId)
draft = MailDraft(m->mailTemplateId,false); // items already included
if(m->HasItems())
{ {
Item *item = pl->GetMItem(itr2->item_guid); for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
if(item)
mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
else
{ {
//WTF? Item *item = pl->GetMItem(itr2->item_guid);
if(item)
draft.AddItem(item);
else
{
//WTF?
}
pl->RemoveMItem(itr2->item_guid);
} }
pl->RemoveMItem(itr2->item_guid);
} }
}
SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId); draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender);
}
delete m; // we can deallocate old mail delete m; // we can deallocate old mail
pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); 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 //called when player takes item attached in mail
void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
{ {
@ -503,7 +444,9 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
// check player existence // check player existence
if(receive || sender_accId) if(receive || sender_accId)
{ {
WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, m->receiver, m->sender, m->subject, 0, NULL, m->COD, 0, MAIL_CHECK_MASK_COD_PAYMENT); MailDraft(m->subject)
.AddMoney(m->COD)
.SendMailTo(MailReceiver(receive,m->sender),MailSender(MAIL_NORMAL,m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
} }
pl->ModifyMoney( -int32(m->COD) ); pl->ModifyMoney( -int32(m->COD) );
@ -608,7 +551,7 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
if ((*itr)->HasItems() && (*itr)->messageType == MAIL_NORMAL) if ((*itr)->HasItems() && (*itr)->messageType == MAIL_NORMAL)
show_flags |= MAIL_SHOW_RETURN; 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 << uint32((*itr)->messageID); // Message ID
data << uint8((*itr)->messageType); // Message Type data << uint8((*itr)->messageType); // Message Type
@ -710,7 +653,9 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
uint64 mailbox; uint64 mailbox;
uint32 mailId; 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)) if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return; return;
@ -718,12 +663,27 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
Player *pl = _player; Player *pl = _player;
Mail* m = pl->GetMail(mailId); 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); pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
return; 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. Item *bodyItem = new Item; // This is not bag and then can be used new Item.
if(!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl)) if(!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl))
{ {
@ -731,7 +691,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
return; 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); bodyItem->SetUInt32Value( ITEM_FIELD_CREATOR, m->sender);
sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId); sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId);
@ -813,90 +773,217 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ )
SendPacket(&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(); uint32 mailId = objmgr.GenerateMailID();
time_t deliver_time = time(NULL) + deliver_delay; time_t deliver_time = time(NULL) + deliver_delay;
uint32 expire_delay; uint32 expire_delay;
// auction mail without any items and money (auction sale note) pending 1 hour // 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; expire_delay = HOUR;
// mail from battlemaster (rewardmarks) should last only one day // 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; expire_delay = DAY;
// default case: expire time if COD 3 days, if no COD 30 days // default case: expire time if COD 3 days, if no COD 30 days
else 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; 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); Item* item = mailItemIter->second;
mailTemplateId = 0; 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; Mail *m = new Mail;
m->messageID = mailId; m->messageID = mailId;
m->messageType = messageType; m->mailTemplateId = GetMailTemplateId();
m->stationery = stationery; m->subject = GetSubject();
m->mailTemplateId = mailTemplateId; m->itemTextId = GetBodyId();
m->sender = sender_guidlow_or_entry; m->money = GetMoney();
m->receiver = receiver->GetGUIDLow(); m->COD = GetCOD();
m->subject = subject;
m->itemTextId = itemTextId;
if (mi) for(MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
m->AddAllItems(*mi); {
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->expire_time = expire_time;
m->deliver_time = deliver_time; m->deliver_time = deliver_time;
m->money = money;
m->COD = COD;
m->checked = checked; m->checked = checked;
m->state = MAIL_STATE_UNCHANGED; 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) for(MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
{ pReceiver->AddMItem(mailItemIter->second);
MailItem& mailItem = mailItemIter->second;
if (mailItem.item)
receiver->AddMItem(mailItem.item);
}
} }
} }
else if (mi) else if (!m_items.empty())
mi->deleteIncludedItems(); deleteIncludedItems();
} }
else if (mi) else if (!m_items.empty())
mi->deleteIncludedItems(); 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();
} }

View file

@ -21,20 +21,12 @@
#include "Common.h" #include "Common.h"
#include <map> #include <map>
struct AuctionEntry;
class Item; class Item;
#define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889 #define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889
#define MAX_MAIL_ITEMS 12 #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 enum MailMessageType
{ {
MAIL_NORMAL = 0, MAIL_NORMAL = 0,
@ -62,16 +54,75 @@ enum MailAuctionAnswers
AUCTION_SALE_PENDING = 6 AUCTION_SALE_PENDING = 6
}; };
// gathered from Stationery.dbc class MailSender
enum MailStationery
{ {
MAIL_STATIONERY_UNKNOWN = 1, public: // Constructors
MAIL_STATIONERY_NORMAL = 41, MailSender(MailMessageType messageType, uint32 sender_guidlow_or_entry, MailStationery stationery = MAIL_STATIONERY_NORMAL)
MAIL_STATIONERY_GM = 61, : m_messageType(messageType), m_senderId(sender_guidlow_or_entry), m_stationery(stationery)
MAIL_STATIONERY_AUCTION = 62, {
MAIL_STATIONERY_VAL = 64, }
MAIL_STATIONERY_CHR = 65, MailSender(Object* sender, MailStationery stationery = MAIL_STATIONERY_NORMAL);
MAIL_STATIONERY_ORP = 67, // new in 3.2.2 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 struct MailItemInfo
@ -80,61 +131,6 @@ struct MailItemInfo
uint32 item_template; 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 struct Mail
{ {
uint32 messageID; uint32 messageID;
@ -162,15 +158,6 @@ struct Mail
items.push_back(mii); 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) bool RemoveItem(uint32 item_guid)
{ {
for(std::vector<MailItemInfo>::iterator itr = items.begin(); itr != items.end(); ++itr) for(std::vector<MailItemInfo>::iterator itr = items.begin(); itr != items.end(); ++itr)
@ -186,4 +173,5 @@ struct Mail
bool HasItems() const { return !items.empty(); } bool HasItems() const { return !items.empty(); }
}; };
#endif #endif

View file

@ -182,22 +182,19 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save,
Guard guard(*this); Guard guard(*this);
// make sure we have a valid map id // make sure we have a valid map id
const MapEntry* entry = sMapStore.LookupEntry(GetId()); if (!sMapStore.LookupEntry(GetId()))
if(!entry)
{ {
sLog.outError("CreateInstance: no entry for map %d", GetId()); sLog.outError("CreateInstance: no entry for map %d", GetId());
assert(false); assert(false);
} }
const InstanceTemplate * iTemplate = objmgr.GetInstanceTemplate(GetId()); if (!objmgr.GetInstanceTemplate(GetId()))
if(!iTemplate)
{ {
sLog.outError("CreateInstance: no instance template for map %d", GetId()); sLog.outError("CreateInstance: no instance template for map %d", GetId());
assert(false); assert(false);
} }
// some instances only have one difficulty // some instances only have one difficulty
MapDifficulty const* mapDiff = GetMapDifficultyData(GetId(),difficulty); if (!GetMapDifficultyData(GetId(),difficulty))
if (!mapDiff)
difficulty = DUNGEON_DIFFICULTY_NORMAL; 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"); 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); 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> template <class T>
void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> &m, uint32 &count, Map* map) 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; 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; T* obj = new T;
//sLog.outString("DEBUG: LoadHelper from table: %s for (guid: %u) Loading",table,guid); //sLog.outString("DEBUG: LoadHelper from table: %s for (guid: %u) Loading",table,guid);
if(!obj->LoadFromDB(guid, map)) if(!obj->LoadFromDB(guid, map))

View file

@ -2163,10 +2163,11 @@ void ObjectMgr::LoadPetLevelInfo()
if (!result) if (!result)
{ {
barGoLink bar( 1 ); barGoLink bar( 1 );
bar.step();
sLog.outString(); sLog.outString();
sLog.outString( ">> Loaded %u level pet stats definitions", count ); sLog.outString(">> Loaded %u level pet stats definitions", count);
sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table."); sLog.outErrorDb("Error loading `pet_levelstats` table or empty table.");
return; return;
} }
@ -3278,6 +3279,9 @@ void ObjectMgr::LoadQuests()
delete result; delete result;
// Post processing // Post processing
std::map<uint32,uint32> usedMailTemplates;
for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter) for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
{ {
Quest * qinfo = iter->second; Quest * qinfo = iter->second;
@ -3766,21 +3770,31 @@ void ObjectMgr::LoadQuests()
} }
} }
if(qinfo->RewMailTemplateId) if (qinfo->RewMailTemplateId)
{ {
if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId)) if (!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
{ {
sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.", sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId); qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
qinfo->RewMailTemplateId = 0; // no mail will send to player qinfo->RewMailTemplateId = 0; // no mail will send to player
qinfo->RewMailDelaySecs = 0; // no mail will send to player qinfo->RewMailDelaySecs = 0; // no mail will send to player
} }
else if (usedMailTemplates.find(qinfo->RewMailTemplateId) != usedMailTemplates.end())
{
std::map<uint32,uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewMailTemplateId);
sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId,used_mt_itr->second);
qinfo->RewMailTemplateId = 0; // no mail will send to player
qinfo->RewMailDelaySecs = 0; // no mail will send to player
}
else
usedMailTemplates[qinfo->RewMailTemplateId] = qinfo->GetQuestId();
} }
if(qinfo->NextQuestInChain) if (qinfo->NextQuestInChain)
{ {
QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain); QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
if(qNextItr == mQuestTemplates.end()) if (qNextItr == mQuestTemplates.end())
{ {
sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.", sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain ); qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
@ -3791,7 +3805,7 @@ void ObjectMgr::LoadQuests()
} }
// fill additional data stores // fill additional data stores
if(qinfo->PrevQuestId) if (qinfo->PrevQuestId)
{ {
if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end()) if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
{ {
@ -7504,6 +7518,72 @@ bool ObjectMgr::DeleteGameTele(const std::string& name)
return false; 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() void ObjectMgr::LoadTrainerSpell()
{ {
// For reload case // For reload case

View file

@ -179,6 +179,19 @@ struct PetLevelInfo
uint16 armor; 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 struct ReputationOnKillEntry
{ {
uint32 repfaction1; uint32 repfaction1;
@ -515,6 +528,7 @@ class ObjectMgr
void LoadNpcOptionLocales(); void LoadNpcOptionLocales();
void LoadPointOfInterestLocales(); void LoadPointOfInterestLocales();
void LoadInstanceTemplate(); void LoadInstanceTemplate();
void LoadMailLevelRewards();
void LoadGossipText(); void LoadGossipText();
@ -584,6 +598,19 @@ class ObjectMgr
typedef std::multimap<int32, uint32> ExclusiveQuestGroups; typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
ExclusiveQuestGroups mExclusiveQuestGroups; 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 WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
{ {
WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id); 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 ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr);
void LoadQuestRelationsHelper(QuestRelations& map,char const* table); void LoadQuestRelationsHelper(QuestRelations& map,char const* table);
MailLevelRewardMap m_mailLevelRewardMap;
typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap; typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
// PetLevelInfoMap[creature_id][level] // PetLevelInfoMap[creature_id][level]
PetLevelInfoMap petInfo; // [creature_id][level] PetLevelInfoMap petInfo; // [creature_id][level]

View file

@ -2452,6 +2452,9 @@ void Player::GiveLevel(uint32 level)
if (Pet* pet = GetPet()) if (Pet* pet = GetPet())
pet->SynchronizeLevelWithOwner(); 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); 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); // remove signs from petitions (also remove petitions if owner);
RemovePetitionsAndSigns(playerguid, 10); RemovePetitionsAndSigns(playerguid, 10);
// return back all mails with COD and Item 0 1 2 3 4 5 6 // return back all mails with COD and Item 0 1 2 3 4 5 6 7
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); 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) if(resultMail)
{ {
do do
@ -3923,18 +3926,30 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
Field *fields = resultMail->Fetch(); Field *fields = resultMail->Fetch();
uint32 mail_id = fields[0].GetUInt32(); uint32 mail_id = fields[0].GetUInt32();
uint16 mailTemplateId= fields[1].GetUInt16(); uint16 mailType = fields[1].GetUInt16();
uint32 sender = fields[2].GetUInt32(); uint16 mailTemplateId= fields[2].GetUInt16();
std::string subject = fields[3].GetCppString(); uint32 sender = fields[3].GetUInt32();
uint32 itemTextId = fields[4].GetUInt32(); std::string subject = fields[4].GetCppString();
uint32 money = fields[5].GetUInt32(); uint32 itemTextId = fields[5].GetUInt32();
bool has_items = fields[6].GetBool(); uint32 money = fields[6].GetUInt32();
bool has_items = fields[7].GetBool();
//we can return mail now //we can return mail now
//so firstly delete the old one //so firstly delete the old one
CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id); 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) if(has_items)
{ {
// data needs to be at first place for Item::LoadFromDB // data needs to be at first place for Item::LoadFromDB
@ -3963,7 +3978,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
continue; continue;
} }
mi.AddItem(item_guidlow, item_template, pItem); draft.AddItem(pItem);
} }
while (resultItems->NextRow()); 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)); 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()); while (resultMail->NextRow());
@ -4816,56 +4831,6 @@ float Player::GetRatingBonusValue(CombatRating cr) const
return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) / GetRatingCoefficient(cr); 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 float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
{ {
switch (attType) switch (attType)
@ -5952,7 +5917,7 @@ void Player::RewardReputation(Quest const *pQuest)
{ {
if(pQuest->RewRepFaction[i] && pQuest->RewRepValue[i] ) 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]); FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]);
if(factionEntry) if(factionEntry)
GetReputationMgr().ModifyReputation(factionEntry, rep); GetReputationMgr().ModifyReputation(factionEntry, rep);
@ -12764,56 +12729,8 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
} }
// Send reward mail // Send reward mail
if (pQuest->GetRewMailTemplateId()) if (uint32 mail_template_id = pQuest->GetRewMailTemplateId())
{ MailDraft(mail_template_id).SendMailTo(this, questGiver, MAIL_CHECK_MASK_NONE, pQuest->GetRewMailDelaySecs());
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 (pQuest->IsDaily()) if (pQuest->IsDaily())
{ {
@ -15040,20 +14957,20 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
// send by mail problematic items // send by mail problematic items
while(!problematicItems.empty()) while(!problematicItems.empty())
{ {
std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
// fill mail // fill mail
MailItemsInfo mi; // item list preparing MailDraft draft(subject);
for(int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i) for(int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
{ {
Item* item = problematicItems.front(); Item* item = problematicItems.front();
problematicItems.pop_front(); problematicItems.pop_front();
mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item); draft.AddItem(item);
} }
std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM); draft.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM));
WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
} }
} }
//if(isAlive()) //if(isAlive())
@ -18921,8 +18838,6 @@ void Player::AutoUnequipOffhandIfNeed()
} }
else else
{ {
MailItemsInfo mi;
mi.AddItem(offItem->GetGUIDLow(), offItem->GetEntry(), offItem);
MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true); MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
CharacterDatabase.BeginTransaction(); CharacterDatabase.BeginTransaction();
offItem->DeleteFromInventoryDB(); // deletes item from character's inventory offItem->DeleteFromInventoryDB(); // deletes item from character's inventory
@ -18930,7 +18845,7 @@ void Player::AutoUnequipOffhandIfNeed()
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_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); MailDraft(subject).AddItem(offItem).SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM));
} }
} }
@ -20806,4 +20721,4 @@ void Player::SendDuelCountdown(uint32 counter)
WorldPacket data(SMSG_DUEL_COUNTDOWN, 4); WorldPacket data(SMSG_DUEL_COUNTDOWN, 4);
data << uint32(counter); // seconds data << uint32(counter); // seconds
GetSession()->SendPacket(&data); GetSession()->SendPacket(&data);
} }

View file

@ -1281,7 +1281,8 @@ class MANGOS_DLL_SPEC Player : public Unit
/*** QUEST SYSTEM ***/ /*** 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 PrepareQuestMenu( uint64 guid );
void SendPreparedQuest( uint64 guid ); void SendPreparedQuest( uint64 guid );
@ -1298,6 +1299,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void CompleteQuest( uint32 quest_id ); void CompleteQuest( uint32 quest_id );
void IncompleteQuest( uint32 quest_id ); void IncompleteQuest( uint32 quest_id );
void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true ); void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true );
void FailQuest( uint32 quest_id ); void FailQuest( uint32 quest_id );
bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg ); bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg );
bool SatisfyQuestLevel( 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 OCTRegenMPPerSpirit();
float GetRatingCoefficient(CombatRating cr) const; float GetRatingCoefficient(CombatRating cr) const;
float GetRatingBonusValue(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; } uint32 GetBaseSpellPowerBonus() { return m_baseSpellPower; }
float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const; float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;

View file

@ -27,7 +27,7 @@ Quest::Quest(Field * questRecord)
ZoneOrSort = questRecord[2].GetInt32(); ZoneOrSort = questRecord[2].GetInt32();
SkillOrClass = questRecord[3].GetInt32(); SkillOrClass = questRecord[3].GetInt32();
MinLevel = questRecord[4].GetUInt32(); MinLevel = questRecord[4].GetUInt32();
QuestLevel = questRecord[5].GetUInt32(); QuestLevel = questRecord[5].GetInt32();
Type = questRecord[6].GetUInt32(); Type = questRecord[6].GetUInt32();
RequiredRaces = questRecord[7].GetUInt32(); RequiredRaces = questRecord[7].GetUInt32();
RequiredSkillValue = questRecord[8].GetUInt32(); RequiredSkillValue = questRecord[8].GetUInt32();
@ -169,7 +169,7 @@ uint32 Quest::XPValue( Player *pPlayer ) const
if( RewMoneyMaxLevel > 0 ) if( RewMoneyMaxLevel > 0 )
{ {
uint32 pLevel = pPlayer->getLevel(); uint32 pLevel = pPlayer->getLevel();
uint32 qLevel = QuestLevel; uint32 qLevel = QuestLevel > 0 ? (uint32)QuestLevel : 0;
float fullxp = 0; float fullxp = 0;
if (qLevel >= 15) if (qLevel >= 15)
fullxp = RewMoneyMaxLevel / 6.0f; fullxp = RewMoneyMaxLevel / 6.0f;

View file

@ -181,7 +181,7 @@ class Quest
int32 GetZoneOrSort() const { return ZoneOrSort; } int32 GetZoneOrSort() const { return ZoneOrSort; }
int32 GetSkillOrClass() const { return SkillOrClass; } int32 GetSkillOrClass() const { return SkillOrClass; }
uint32 GetMinLevel() const { return MinLevel; } uint32 GetMinLevel() const { return MinLevel; }
uint32 GetQuestLevel() const { return QuestLevel; } int32 GetQuestLevel() const { return QuestLevel; }
uint32 GetType() const { return Type; } uint32 GetType() const { return Type; }
uint32 GetRequiredRaces() const { return RequiredRaces; } uint32 GetRequiredRaces() const { return RequiredRaces; }
uint32 GetRequiredSkillValue() const { return RequiredSkillValue; } uint32 GetRequiredSkillValue() const { return RequiredSkillValue; }
@ -274,7 +274,7 @@ class Quest
int32 ZoneOrSort; int32 ZoneOrSort;
int32 SkillOrClass; int32 SkillOrClass;
uint32 MinLevel; uint32 MinLevel;
uint32 QuestLevel; int32 QuestLevel;
uint32 Type; uint32 Type;
uint32 RequiredRaces; uint32 RequiredRaces;
uint32 RequiredSkillValue; 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)) if ( pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->getQuestStatusMap()[quest_id].m_rewarded))
result2 = DIALOG_STATUS_REWARD_REP; 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)) if (pQuest->HasFlag(QUEST_FLAGS_DAILY))
result2 = DIALOG_STATUS_AVAILABLE_REP; result2 = DIALOG_STATUS_AVAILABLE_REP;

View file

@ -2387,10 +2387,10 @@ enum PetDiet
#define CHAIN_SPELL_JUMP_RADIUS 10 #define CHAIN_SPELL_JUMP_RADIUS 10
// Max values for Guild & Guild Bank // 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_SLOTS 98
#define GUILD_BANK_MAX_LOGS 25 #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_EVENTLOG_MAX_RECORDS 100
#define GUILD_RANKS_MIN_COUNT 5 #define GUILD_RANKS_MIN_COUNT 5
#define GUILD_RANKS_MAX_COUNT 10 #define GUILD_RANKS_MAX_COUNT 10
@ -2620,6 +2620,27 @@ enum BattleGroundTypeId
}; };
#define MAX_BATTLEGROUND_TYPE_ID 33 #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 enum MailResponseType
{ {
MAIL_SEND = 0, MAIL_SEND = 0,

View file

@ -6382,12 +6382,12 @@ void Aura::PeriodicTick()
if (isCrit) if (isCrit)
cleanDamage.hitOutCome = MELEE_HIT_CRIT; cleanDamage.hitOutCome = MELEE_HIT_CRIT;
// Reduce dot damage from resilience for players. // only from players
// FIXME: need use SpellDamageBonus instead? // FIXME: need use SpellDamageBonus instead?
if (m_target->GetTypeId() == TYPEID_PLAYER) if (IS_PLAYER_GUID(m_caster_guid))
pdamage-=((Player*)m_target)->GetSpellDamageReduction(pdamage); 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", 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); 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()); 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) if(m_target->GetHealth() < pdamage)
pdamage = uint32(m_target->GetHealth()); 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); 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) // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
if (power == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER) if (power == POWER_MANA)
drain_amount -= ((Player*)m_target)->GetSpellCritDamageReduction(drain_amount); drain_amount -= m_target->GetSpellCritDamageReduction(drain_amount);
m_target->ModifyPower(power, -drain_amount); m_target->ModifyPower(power, -drain_amount);
@ -6701,8 +6701,8 @@ void Aura::PeriodicTick()
return; return;
// resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
if (powerType == POWER_MANA && m_target->GetTypeId() == TYPEID_PLAYER) if (powerType == POWER_MANA)
pdamage -= ((Player*)m_target)->GetSpellCritDamageReduction(pdamage); pdamage -= m_target->GetSpellCritDamageReduction(pdamage);
uint32 gain = uint32(-m_target->ModifyPower(powerType, -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) // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
uint32 power = damage; uint32 power = damage;
if ( drain_power == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER ) if (drain_power == POWER_MANA)
power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); power -= unitTarget->GetSpellCritDamageReduction(power);
int32 new_damage; int32 new_damage;
if(curPower < power) 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) // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
uint32 power = damage; uint32 power = damage;
if (powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER) if (powertype == POWER_MANA)
power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); power -= unitTarget->GetSpellCritDamageReduction(power);
int32 new_damage = (curPower < power) ? curPower : 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; damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT;
damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim);
// Resilience - reduce crit damage // Resilience - reduce crit damage
if (pVictim->GetTypeId()==TYPEID_PLAYER) uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask);
{ if (attackType != RANGED_ATTACK)
uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); damage -= pVictim->GetMeleeCritDamageReduction(redunction_affected_damage);
damage -= ((Player*)pVictim)->GetMeleeCritDamageReduction(redunction_affected_damage); else
} damage -= pVictim->GetRangedCritDamageReduction(redunction_affected_damage);
} }
} }
break; break;
@ -1080,20 +1080,18 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S
damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT;
damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim);
// Resilience - reduce crit damage // Resilience - reduce crit damage
if (pVictim->GetTypeId()==TYPEID_PLAYER) uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask);
{ damage -= pVictim->GetSpellCritDamageReduction(redunction_affected_damage);
uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask);
damage -= ((Player*)pVictim)->GetSpellCritDamageReduction(redunction_affected_damage);
}
} }
} }
break; break;
} }
if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) // only from players
if (GetTypeId() == TYPEID_PLAYER)
{ {
uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask);
damage -= ((Player*)pVictim)->GetSpellDamageReduction(redunction_affected_damage); damage -= pVictim->GetSpellDamageReduction(redunction_affected_damage);
} }
// damage mitigation // damage mitigation
@ -1116,7 +1114,7 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S
} }
uint32 absorb_affected_damage = CalcNotIgnoreAbsorbDamage(damage,damageSchoolMask,spellInfo); 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; damage-= damageInfo->absorb + damageInfo->resist;
} }
else 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)); damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f));
// Resilience - reduce crit damage // Resilience - reduce crit damage
if (pVictim->GetTypeId()==TYPEID_PLAYER) uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damageInfo->damage,damageInfo->damageSchoolMask);
{ uint32 resilienceReduction;
uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damageInfo->damage,damageInfo->damageSchoolMask); if (attackType != RANGED_ATTACK)
uint32 resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(redunction_affected_damage); resilienceReduction = pVictim->GetMeleeCritDamageReduction(redunction_affected_damage);
damageInfo->damage -= resilienceReduction; else
damageInfo->cleanDamage += resilienceReduction; resilienceReduction = pVictim->GetRangedCritDamageReduction(redunction_affected_damage);
}
damageInfo->damage -= resilienceReduction;
damageInfo->cleanDamage += resilienceReduction;
break; break;
} }
case MELEE_HIT_PARRY: case MELEE_HIT_PARRY:
@ -1391,13 +1391,14 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
break; break;
} }
if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) // only from players
if (GetTypeId() == TYPEID_PLAYER)
{ {
uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageInfo->damageSchoolMask); uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageInfo->damageSchoolMask);
if (attackType != RANGED_ATTACK) if (attackType != RANGED_ATTACK)
damage-=((Player*)pVictim)->GetMeleeDamageReduction(redunction_affected_damage); damage -= pVictim->GetMeleeDamageReduction(redunction_affected_damage);
else else
damage-=((Player*)pVictim)->GetRangedDamageReduction(redunction_affected_damage); damage -= pVictim->GetRangedDamageReduction(redunction_affected_damage);
} }
// Calculate absorb resist // Calculate absorb resist
@ -1407,7 +1408,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
// Calculate absorb & resists // Calculate absorb & resists
uint32 absorb_affected_damage = CalcNotIgnoreAbsorbDamage(damageInfo->damage,damageInfo->damageSchoolMask); 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; damageInfo->damage-=damageInfo->absorb + damageInfo->resist;
if (damageInfo->absorb) if (damageInfo->absorb)
{ {
@ -1627,7 +1628,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage)
return (newdamage > 1) ? newdamage : 1; 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) if(!pVictim || !pVictim->isAlive() || !damage)
return; return;
@ -1731,7 +1732,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
continue; continue;
} }
// Reflective Shield (Lady Malande boss) // Reflective Shield (Lady Malande boss)
if (spellProto->Id == 41475) if (spellProto->Id == 41475 && canReflect)
{ {
if(RemainingDamage < currentAbsorb) if(RemainingDamage < currentAbsorb)
reflectDamage = RemainingDamage / 2; reflectDamage = RemainingDamage / 2;
@ -1789,7 +1790,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
} }
// Reflective Shield // Reflective Shield
if (spellProto->SpellFamilyFlags == 0x1) if (spellProto->SpellFamilyFlags == 0x1 && canReflect)
{ {
if (pVictim == this) if (pVictim == this)
break; break;
@ -1911,7 +1912,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
} }
// Cast back reflect damage spell // Cast back reflect damage spell
if (reflectSpell) if (canReflect && reflectSpell)
pVictim->CastCustomSpell(this, reflectSpell, &reflectDamage, NULL, NULL, true, NULL, reflectTriggeredBy); pVictim->CastCustomSpell(this, reflectSpell, &reflectDamage, NULL, NULL, true, NULL, reflectTriggeredBy);
// absorb by mana cost // 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); crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
// reduce crit chance from Rating for players // reduce crit chance from Rating for players
if (pVictim->GetTypeId()==TYPEID_PLAYER) if (attackType != RANGED_ATTACK)
{ crit -= pVictim->GetMeleeCritChanceReduction();
if (attackType==RANGED_ATTACK) else
crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_RANGED); crit -= pVictim->GetRangedCritChanceReduction();
else
crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE);
}
// Apply crit chance from defence skill // Apply crit chance from defence skill
crit += (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f; 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 // 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); crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
// Modify by player victim resilience // Modify by player victim resilience
if (pVictim->GetTypeId() == TYPEID_PLAYER) crit_chance -= pVictim->GetSpellCritChanceReduction();
crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
} }
// scripted (increase crit chance ... against ... target by x%) // 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)) if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE))
return false; 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 false;
return IsInWorld() && !hasUnitState(UNIT_STAT_DIED) && !isInFlight(); return IsInWorld() && !hasUnitState(UNIT_STAT_DIED) && !isInFlight();
@ -9788,8 +9786,11 @@ bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo
return false; return false;
} }
if (u->isAlive() && isInvisibleForAlive()) // isInvisibleForAlive() those units can only be seen by dead or if other
if (u->GetTypeId() == TYPEID_PLAYER && !((Player *)u)->isGameMaster()) // 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; return false;
// Visible units, always are visible for all units, except for units under invisibility and phases // 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); 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 CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK);
void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss); 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); float MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell);
SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell); SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell);
SpellMissInfo MagicSpellHitResult(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 // redefined in Creature
uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage); 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); void UpdateSpeed(UnitMoveType mtype, bool forced);
float GetSpeed( UnitMoveType mtype ) const; 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 HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 cooldown);
bool HandleMendingAuraProc(Aura* triggeredByAura); 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_state; // Even derived shouldn't modify
uint32 m_CombatTimer; uint32 m_CombatTimer;

View file

@ -1106,6 +1106,9 @@ void World::SetInitialWorldSettings()
///- Initialize the random number generator ///- Initialize the random number generator
srand((unsigned int)time(NULL)); srand((unsigned int)time(NULL));
///- Time server startup
uint32 uStartTime = getMSTime();
///- Initialize config settings ///- Initialize config settings
LoadConfigSettings(); LoadConfigSettings();
@ -1335,6 +1338,9 @@ void World::SetInitialWorldSettings()
sLog.outString( "Loading Player Corpses..." ); sLog.outString( "Loading Player Corpses..." );
objmgr.LoadCorpses(); objmgr.LoadCorpses();
sLog.outString( "Loading Player level dependent mail rewards..." );
objmgr.LoadMailLevelRewards();
sLog.outString( "Loading Loot Tables..." ); sLog.outString( "Loading Loot Tables..." );
sLog.outString(); sLog.outString();
LoadLootTables(); LoadLootTables();
@ -1508,6 +1514,9 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event
sLog.outString( "WORLD: World initialized" ); 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() void World::DetectDBCLang()

View file

@ -26,7 +26,6 @@
#include "Common.h" #include "Common.h"
#include "SharedDefines.h" #include "SharedDefines.h"
class MailItemsInfo;
struct ItemPrototype; struct ItemPrototype;
struct AuctionEntry; struct AuctionEntry;
struct DeclinedName; struct DeclinedName;
@ -39,7 +38,6 @@ class Player;
class Unit; class Unit;
class WorldPacket; class WorldPacket;
class WorldSocket; class WorldSocket;
class WorldSession;
class QueryResult; class QueryResult;
class LoginQueryHolder; class LoginQueryHolder;
class CharacterHandler; class CharacterHandler;
@ -220,12 +218,8 @@ class MANGOS_DLL_SPEC WorldSession
m_TutorialsChanged = true; m_TutorialsChanged = true;
} }
} }
//used with item_page table
//mail
//used with item_page table
bool SendItemInfo( uint32 itemid, WorldPacket data ); 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 //auction
void SendAuctionHello( uint64 guid, Creature * unit ); void SendAuctionHello( uint64 guid, Creature * unit );

View file

@ -43,10 +43,9 @@ mangos_worldd_LDADD = \
../shared/vmap/libmangosvmaps.a \ ../shared/vmap/libmangosvmaps.a \
../framework/libmangosframework.a \ ../framework/libmangosframework.a \
../../dep/src/sockets/libmangossockets.a \ ../../dep/src/sockets/libmangossockets.a \
../../dep/src/g3dlite/libg3dlite.a \ ../../dep/src/g3dlite/libg3dlite.a
../../dep/tbb/libtbbmalloc.so
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' ## Additional files to include when running 'make dist'
# Include world daemon configuration # Include world daemon configuration

View file

@ -36,10 +36,9 @@ mangos_realmd_LDADD = \
../shared/Auth/libmangosauth.a \ ../shared/Auth/libmangosauth.a \
../shared/libmangosshared.a \ ../shared/libmangosshared.a \
../framework/libmangosframework.a \ ../framework/libmangosframework.a \
../../dep/src/sockets/libmangossockets.a \ ../../dep/src/sockets/libmangossockets.a
../../dep/tbb/libtbbmalloc.so
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' ## Additional files to include when running 'make dist'
# Include realm list daemon configuration # Include realm list daemon configuration

View file

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

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__ #ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__ #define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_8721_01_characters_guild" #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" #define REVISION_DB_REALMD "required_8728_01_realmd_account"
#endif // __REVISION_SQL_H__ #endif // __REVISION_SQL_H__

View file

@ -143,7 +143,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -170,7 +170,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -194,7 +194,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <StringPooling>true</StringPooling>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@ -221,7 +221,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <StringPooling>true</StringPooling>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@ -245,7 +245,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -272,7 +272,7 @@
<AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>/Zl /MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\src\framework;..\..\dep\ACE_wrappers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>