2010-10-07 15:35:36 +02:00
/*
2012-12-31 23:15:50 +01:00
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
2010-10-07 15:35:36 +02:00
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
2010-09-04 13:46:01 -07:00
*/
#include "DatabaseEnv.h"
#include "Mail.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "Language.h"
#include "DBCStores.h"
#include "Item.h"
2011-09-08 15:11:55 +02:00
#include "AccountMgr.h"
2010-09-04 13:46:01 -07:00
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleSendMail ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox , unk3 ;
std :: string receiver , subject , body ;
uint32 unk1 , unk2 , money , COD ;
uint8 unk4 ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> receiver ;
2010-09-04 13:46:01 -07:00
2012-11-02 22:23:14 +01:00
recvData >> subject ;
2010-09-04 13:46:01 -07:00
2012-11-02 22:23:14 +01:00
recvData >> body ;
2010-09-04 13:46:01 -07:00
2012-11-02 22:23:14 +01:00
recvData >> unk1 ; // stationery?
recvData >> unk2 ; // 0x00000000
2010-09-04 13:46:01 -07:00
uint8 items_count ;
2012-11-02 22:23:14 +01:00
recvData >> items_count ; // attached items count
2010-09-04 13:46:01 -07:00
if ( items_count > MAX_MAIL_ITEMS ) // client limit
{
GetPlayer () -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_TOO_MANY_ATTACHMENTS );
2012-11-02 22:23:14 +01:00
recvData . rfinish (); // set to end to avoid warnings spam
2010-09-04 13:46:01 -07:00
return ;
}
uint64 itemGUIDs [ MAX_MAIL_ITEMS ];
for ( uint8 i = 0 ; i < items_count ; ++ i )
{
2012-11-02 22:23:14 +01:00
recvData . read_skip < uint8 > (); // item slot in mail, not used
recvData >> itemGUIDs [ i ];
2010-09-04 13:46:01 -07:00
}
2012-11-02 22:23:14 +01:00
recvData >> money >> COD ; // money and cod
recvData >> unk3 ; // const 0
recvData >> unk4 ; // const 0
2010-09-04 13:46:01 -07:00
// packet read complete, now do check
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
if ( receiver . empty ())
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
if ( player -> getLevel () < sWorld -> getIntConfig ( CONFIG_MAIL_LEVEL_REQ ))
2010-09-04 13:46:01 -07:00
{
2010-12-23 23:25:44 +01:00
SendNotification ( GetTrinityString ( LANG_MAIL_SENDER_REQ ), sWorld -> getIntConfig ( CONFIG_MAIL_LEVEL_REQ ));
2010-09-04 13:46:01 -07:00
return ;
}
uint64 rc = 0 ;
if ( normalizePlayerName ( receiver ))
2010-12-22 21:25:23 +01:00
rc = sObjectMgr -> GetPlayerGUIDByName ( receiver );
2010-09-04 13:46:01 -07:00
if ( ! rc )
{
2012-08-03 14:20:18 +02:00
sLog -> outInfo ( LOG_FILTER_NETWORKIO , "Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u" ,
2011-11-07 11:18:00 -06:00
player -> GetGUIDLow (), receiver . c_str (), subject . c_str (), body . c_str (), items_count , money , COD , unk1 , unk2 );
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_RECIPIENT_NOT_FOUND );
2010-09-04 13:46:01 -07:00
return ;
}
2012-08-03 14:20:18 +02:00
sLog -> outInfo ( LOG_FILTER_NETWORKIO , "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" , player -> GetGUIDLow (), receiver . c_str (), GUID_LOPART ( rc ), subject . c_str (), body . c_str (), items_count , money , COD , unk1 , unk2 );
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
if ( player -> GetGUID () == rc )
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_CANNOT_SEND_TO_SELF );
2010-09-04 13:46:01 -07:00
return ;
}
uint32 cost = items_count ? 30 * items_count : 30 ; // price hardcoded in client
uint32 reqmoney = cost + money ;
2012-06-23 01:22:09 +02:00
if ( ! player -> HasEnoughMoney ( reqmoney ) && ! player -> isGameMaster ())
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_NOT_ENOUGH_MONEY );
2010-09-04 13:46:01 -07:00
return ;
}
2011-09-15 14:08:17 +02:00
Player * receive = ObjectAccessor :: FindPlayer ( rc );
2010-09-04 13:46:01 -07:00
uint32 rc_team = 0 ;
uint8 mails_count = 0 ; //do not allow to send to one player more than 100 mails
uint8 receiveLevel = 0 ;
if ( receive )
{
rc_team = receive -> GetTeam ();
mails_count = receive -> GetMailSize ();
receiveLevel = receive -> getLevel ();
}
else
{
2010-12-22 21:25:23 +01:00
rc_team = sObjectMgr -> GetPlayerTeamByGUID ( rc );
2012-03-24 01:25:08 +01:00
PreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_SEL_MAIL_COUNT );
stmt -> setUInt32 ( 0 , GUID_LOPART ( rc ));
PreparedQueryResult result = CharacterDatabase . Query ( stmt );
if ( result )
2010-09-04 13:46:01 -07:00
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2012-03-31 17:47:18 +01:00
mails_count = fields [ 0 ]. GetUInt64 ();
2010-09-04 13:46:01 -07:00
}
2012-03-24 01:25:08 +01:00
stmt = CharacterDatabase . GetPreparedStatement ( CHAR_SEL_CHAR_LEVEL );
stmt -> setUInt32 ( 0 , GUID_LOPART ( rc ));
result = CharacterDatabase . Query ( stmt );
if ( result )
2010-09-04 13:46:01 -07:00
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2010-09-04 13:46:01 -07:00
receiveLevel = fields [ 0 ]. GetUInt8 ();
}
}
//do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255..
if ( mails_count > 100 )
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_RECIPIENT_CAP_REACHED );
2010-09-04 13:46:01 -07:00
return ;
}
// test the receiver's Faction... or all items are account bound
bool accountBound = items_count ? true : false ;
for ( uint8 i = 0 ; i < items_count ; ++ i )
{
2011-11-07 11:18:00 -06:00
Item * item = player -> GetItemByGuid ( itemGUIDs [ i ]);
2010-09-04 13:46:01 -07:00
if ( item )
{
2011-04-28 22:42:33 +02:00
ItemTemplate const * itemProto = item -> GetTemplate ();
2011-06-12 02:30:32 +02:00
if ( ! itemProto || ! ( itemProto -> Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT ))
2010-09-04 13:46:01 -07:00
{
accountBound = false ;
break ;
}
}
}
2011-11-07 11:18:00 -06:00
if ( ! accountBound && ! sWorld -> getBoolConfig ( CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL ) && player -> GetTeam () != rc_team && AccountMgr :: IsPlayerAccount ( GetSecurity ()))
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_NOT_YOUR_TEAM );
2010-09-04 13:46:01 -07:00
return ;
}
2010-12-23 23:25:44 +01:00
if ( receiveLevel < sWorld -> getIntConfig ( CONFIG_MAIL_LEVEL_REQ ))
2010-09-04 13:46:01 -07:00
{
2010-12-23 23:25:44 +01:00
SendNotification ( GetTrinityString ( LANG_MAIL_RECEIVER_REQ ), sWorld -> getIntConfig ( CONFIG_MAIL_LEVEL_REQ ));
2010-09-04 13:46:01 -07:00
return ;
}
uint32 rc_account = receive
? receive -> GetSession () -> GetAccountId ()
2010-12-22 21:25:23 +01:00
: sObjectMgr -> GetPlayerAccountIdByGUID ( rc );
2010-09-04 13:46:01 -07:00
Item * items [ MAX_MAIL_ITEMS ];
for ( uint8 i = 0 ; i < items_count ; ++ i )
{
if ( ! itemGUIDs [ i ])
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_MAIL_ATTACHMENT_INVALID );
2010-09-04 13:46:01 -07:00
return ;
}
2011-11-07 11:18:00 -06:00
Item * item = player -> GetItemByGuid ( itemGUIDs [ i ]);
2010-09-04 13:46:01 -07:00
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if ( ! item )
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_MAIL_ATTACHMENT_INVALID );
2010-09-04 13:46:01 -07:00
return ;
}
if ( ! item -> CanBeTraded ( true ))
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_EQUIP_ERROR , EQUIP_ERR_MAIL_BOUND_ITEM );
2010-09-04 13:46:01 -07:00
return ;
}
2011-11-07 11:18:00 -06:00
if ( item -> IsBoundAccountWide () && item -> IsSoulBound () && player -> GetSession () -> GetAccountId () != rc_account )
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_EQUIP_ERROR , EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS );
2010-09-04 13:46:01 -07:00
return ;
}
2011-04-28 22:42:33 +02:00
if ( item -> GetTemplate () -> Flags & ITEM_PROTO_FLAG_CONJURED || item -> GetUInt32Value ( ITEM_FIELD_DURATION ))
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_EQUIP_ERROR , EQUIP_ERR_MAIL_BOUND_ITEM );
2010-09-04 13:46:01 -07:00
return ;
}
if ( COD && item -> HasFlag ( ITEM_FIELD_FLAGS , ITEM_FLAG_WRAPPED ))
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_CANT_SEND_WRAPPED_COD );
2010-09-04 13:46:01 -07:00
return ;
}
2011-03-18 15:58:08 +06:00
if ( item -> IsNotEmptyBag ())
2010-10-09 17:36:53 +02:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_ERR_EQUIP_ERROR , EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS );
2010-10-09 17:36:53 +02:00
return ;
}
2010-09-04 13:46:01 -07:00
items [ i ] = item ;
}
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( 0 , MAIL_SEND , MAIL_OK );
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
player -> ModifyMoney ( - int32 ( reqmoney ));
2012-09-18 14:29:53 +02:00
player -> UpdateAchievementCriteria ( ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL , cost );
2010-09-04 13:46:01 -07:00
bool needItemDelay = false ;
MailDraft draft ( subject , body );
SQLTransaction trans = CharacterDatabase . BeginTransaction ();
if ( items_count > 0 || money > 0 )
{
if ( items_count > 0 )
{
for ( uint8 i = 0 ; i < items_count ; ++ i )
{
Item * item = items [ i ];
2011-09-08 15:11:55 +02:00
if ( ! AccountMgr :: IsPlayerAccount ( GetSecurity ()) && sWorld -> getBoolConfig ( CONFIG_GM_LOG_TRADE ))
2010-09-04 13:46:01 -07:00
{
2010-12-23 23:25:44 +01:00
sLog -> outCommand ( GetAccountId (), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)" ,
2012-08-21 22:12:55 +02:00
GetPlayerName (). c_str (), GetAccountId (), item -> GetTemplate () -> Name1 . c_str (), item -> GetEntry (), item -> GetCount (), receiver . c_str (), rc_account );
2010-09-04 13:46:01 -07:00
}
item -> SetNotRefundable ( GetPlayer ()); // makes the item no longer refundable
2011-11-07 11:18:00 -06:00
player -> MoveItemFromInventory ( items [ i ] -> GetBagSlot (), item -> GetSlot (), true );
2010-09-04 13:46:01 -07:00
item -> DeleteFromInventoryDB ( trans ); // deletes item from character's inventory
2011-05-23 16:33:33 +02:00
item -> SetOwnerGUID ( rc );
2010-09-04 13:46:01 -07:00
item -> SaveToDB ( trans ); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
2010-12-15 14:08:12 +06:00
2010-09-04 13:46:01 -07:00
draft . AddItem ( item );
}
// if item send to character at another account, then apply item delivery delay
2011-11-07 11:18:00 -06:00
needItemDelay = player -> GetSession () -> GetAccountId () != rc_account ;
2010-09-04 13:46:01 -07:00
}
2011-09-08 15:11:55 +02:00
if ( money > 0 && ! AccountMgr :: IsPlayerAccount ( GetSecurity ()) && sWorld -> getBoolConfig ( CONFIG_GM_LOG_TRADE ))
2010-09-04 13:46:01 -07:00
{
2011-04-29 20:47:02 +02:00
sLog -> outCommand ( GetAccountId (), "GM %s (Account: %u) mail money: %u to player: %s (Account: %u)" ,
2012-08-21 22:12:55 +02:00
GetPlayerName (). c_str (), GetAccountId (), money , receiver . c_str (), rc_account );
2010-09-04 13:46:01 -07:00
}
}
// If theres is an item, there is a one hour delivery delay if sent to another account's character.
2010-12-23 23:25:44 +01:00
uint32 deliver_delay = needItemDelay ? sWorld -> getIntConfig ( CONFIG_MAIL_DELIVERY_DELAY ) : 0 ;
2010-09-04 13:46:01 -07:00
// will delete item or place to receiver mail list
draft
. AddMoney ( money )
. AddCOD ( COD )
2011-11-07 11:18:00 -06:00
. SendMailTo ( trans , MailReceiver ( receive , GUID_LOPART ( rc )), MailSender ( player ), body . empty () ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY , deliver_delay );
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
player -> SaveInventoryAndGoldToDB ( trans );
2010-09-04 13:46:01 -07:00
CharacterDatabase . CommitTransaction ( trans );
}
//called when mail is read
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleMailMarkAsRead ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
uint32 mailId ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> mailId ;
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
Mail * m = player -> GetMail ( mailId );
2010-09-04 13:46:01 -07:00
if ( m )
{
2011-11-07 11:18:00 -06:00
if ( player -> unReadMails )
-- player -> unReadMails ;
2010-09-04 13:46:01 -07:00
m -> checked = m -> checked | MAIL_CHECK_MASK_READ ;
2011-11-07 11:18:00 -06:00
player -> m_mailsUpdated = true ;
2010-09-04 13:46:01 -07:00
m -> state = MAIL_STATE_CHANGED ;
}
}
//called when client deletes mail
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleMailDelete ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
uint32 mailId ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> mailId ;
recvData . read_skip < uint32 > (); // mailTemplateId
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-09-15 14:08:17 +02:00
Mail * m = _player -> GetMail ( mailId );
2011-11-07 11:18:00 -06:00
Player * player = _player ;
player -> m_mailsUpdated = true ;
2010-09-04 13:46:01 -07:00
if ( m )
{
// delete shouldn't show up for COD mails
if ( m -> COD )
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_DELETED , MAIL_ERR_INTERNAL_ERROR );
2010-09-04 13:46:01 -07:00
return ;
}
m -> state = MAIL_STATE_DELETED ;
}
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_DELETED , MAIL_OK );
2010-09-04 13:46:01 -07:00
}
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleMailReturnToSender ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
uint32 mailId ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> mailId ;
recvData . read_skip < uint64 > (); // original sender GUID for return to, not used
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
Mail * m = player -> GetMail ( mailId );
2010-09-04 13:46:01 -07:00
if ( ! m || m -> state == MAIL_STATE_DELETED || m -> deliver_time > time ( NULL ))
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_RETURNED_TO_SENDER , MAIL_ERR_INTERNAL_ERROR );
2010-09-04 13:46:01 -07:00
return ;
}
//we can return mail now
//so firstly delete the old one
SQLTransaction trans = CharacterDatabase . BeginTransaction ();
2012-03-25 16:22:24 +02:00
2012-03-26 15:46:54 +02:00
PreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_DEL_MAIL_BY_ID );
2012-03-25 16:22:24 +02:00
stmt -> setUInt32 ( 0 , mailId );
trans -> Append ( stmt );
stmt = CharacterDatabase . GetPreparedStatement ( CHAR_DEL_MAIL_ITEM_BY_ID );
stmt -> setUInt32 ( 0 , mailId );
trans -> Append ( stmt );
2011-11-07 11:18:00 -06:00
player -> RemoveMail ( mailId );
2010-09-04 13:46:01 -07:00
// only return mail if the player exists (and delete if not existing)
if ( m -> messageType == MAIL_NORMAL && m -> sender )
{
MailDraft draft ( m -> subject , m -> body );
if ( m -> mailTemplateId )
draft = MailDraft ( m -> mailTemplateId , false ); // items already included
if ( m -> HasItems ())
{
2010-12-15 14:08:12 +06:00
for ( MailItemInfoVec :: iterator itr2 = m -> items . begin (); itr2 != m -> items . end (); ++ itr2 )
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
Item * item = player -> GetMItem ( itr2 -> item_guid );
2010-09-04 13:46:01 -07:00
if ( item )
draft . AddItem ( item );
else
{
//WTF?
}
2011-11-07 11:18:00 -06:00
player -> RemoveMItem ( itr2 -> item_guid );
2010-09-04 13:46:01 -07:00
}
}
2011-05-21 14:24:46 +02:00
draft . AddMoney ( m -> money ). SendReturnToSender ( GetAccountId (), m -> receiver , m -> sender , trans );
2010-09-04 13:46:01 -07:00
}
2011-05-21 14:24:46 +02:00
CharacterDatabase . CommitTransaction ( trans );
2010-09-04 13:46:01 -07:00
delete m ; //we can deallocate old mail
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_RETURNED_TO_SENDER , MAIL_OK );
2010-09-04 13:46:01 -07:00
}
//called when player takes item attached in mail
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleMailTakeItem ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
uint32 mailId ;
uint32 itemId ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> mailId ;
recvData >> itemId ; // item guid low
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
Mail * m = player -> GetMail ( mailId );
2010-09-04 13:46:01 -07:00
if ( ! m || m -> state == MAIL_STATE_DELETED || m -> deliver_time > time ( NULL ))
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_ITEM_TAKEN , MAIL_ERR_INTERNAL_ERROR );
2010-09-04 13:46:01 -07:00
return ;
}
// prevent cheating with skip client money check
2011-11-07 11:18:00 -06:00
if ( ! player -> HasEnoughMoney ( m -> COD ))
2010-09-04 13:46:01 -07:00
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_ITEM_TAKEN , MAIL_ERR_NOT_ENOUGH_MONEY );
2010-09-04 13:46:01 -07:00
return ;
}
2011-11-07 11:18:00 -06:00
Item * it = player -> GetMItem ( itemId );
2010-09-04 13:46:01 -07:00
ItemPosCountVec dest ;
uint8 msg = _player -> CanStoreItem ( NULL_BAG , NULL_SLOT , dest , it , false );
if ( msg == EQUIP_ERR_OK )
{
SQLTransaction trans = CharacterDatabase . BeginTransaction ();
m -> RemoveItem ( itemId );
m -> removedItems . push_back ( itemId );
if ( m -> COD > 0 ) //if there is COD, take COD money from player and send them to sender by mail
{
uint64 sender_guid = MAKE_NEW_GUID ( m -> sender , 0 , HIGHGUID_PLAYER );
2011-09-15 14:08:17 +02:00
Player * receive = ObjectAccessor :: FindPlayer ( sender_guid );
2010-09-04 13:46:01 -07:00
uint32 sender_accId = 0 ;
2011-09-08 15:11:55 +02:00
if ( ! AccountMgr :: IsPlayerAccount ( GetSecurity ()) && sWorld -> getBoolConfig ( CONFIG_GM_LOG_TRADE ))
2010-09-04 13:46:01 -07:00
{
std :: string sender_name ;
if ( receive )
{
sender_accId = receive -> GetSession () -> GetAccountId ();
sender_name = receive -> GetName ();
}
else
{
// can be calculated early
2010-12-22 21:25:23 +01:00
sender_accId = sObjectMgr -> GetPlayerAccountIdByGUID ( sender_guid );
2010-09-04 13:46:01 -07:00
2011-04-29 20:47:02 +02:00
if ( ! sObjectMgr -> GetPlayerNameByGUID ( sender_guid , sender_name ))
2010-12-22 21:25:23 +01:00
sender_name = sObjectMgr -> GetTrinityStringForDBCLocale ( LANG_UNKNOWN );
2010-09-04 13:46:01 -07:00
}
2011-04-29 20:47:02 +02:00
sLog -> outCommand ( GetAccountId (), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)" ,
2012-08-21 22:12:55 +02:00
GetPlayerName (). c_str (), GetAccountId (), it -> GetTemplate () -> Name1 . c_str (), it -> GetEntry (), it -> GetCount (), m -> COD , sender_name . c_str (), sender_accId );
2010-09-04 13:46:01 -07:00
}
else if ( ! receive )
2010-12-22 21:25:23 +01:00
sender_accId = sObjectMgr -> GetPlayerAccountIdByGUID ( sender_guid );
2010-09-04 13:46:01 -07:00
// check player existence
if ( receive || sender_accId )
{
MailDraft ( m -> subject , "" )
. AddMoney ( m -> COD )
. SendMailTo ( trans , MailReceiver ( receive , m -> sender ), MailSender ( MAIL_NORMAL , m -> receiver ), MAIL_CHECK_MASK_COD_PAYMENT );
}
2011-11-07 11:18:00 -06:00
player -> ModifyMoney ( - int32 ( m -> COD ));
2010-09-04 13:46:01 -07:00
}
m -> COD = 0 ;
m -> state = MAIL_STATE_CHANGED ;
2011-11-07 11:18:00 -06:00
player -> m_mailsUpdated = true ;
player -> RemoveMItem ( it -> GetGUIDLow ());
2010-09-04 13:46:01 -07:00
uint32 count = it -> GetCount (); // save counts before store and possible merge with deleting
2012-09-13 03:15:22 +02:00
it -> SetState ( ITEM_UNCHANGED ); // need to set this state, otherwise item cannot be removed later, if neccessary
2011-11-07 11:18:00 -06:00
player -> MoveItemToInventory ( dest , it , true );
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
player -> SaveInventoryAndGoldToDB ( trans );
player -> _SaveMail ( trans );
2010-09-04 13:46:01 -07:00
CharacterDatabase . CommitTransaction ( trans );
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_ITEM_TAKEN , MAIL_OK , 0 , itemId , count );
2010-09-04 13:46:01 -07:00
}
else
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_ITEM_TAKEN , MAIL_ERR_EQUIP_ERROR , msg );
2010-09-04 13:46:01 -07:00
}
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleMailTakeMoney ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
uint32 mailId ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> mailId ;
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
Mail * m = player -> GetMail ( mailId );
2010-09-04 13:46:01 -07:00
if ( ! m || m -> state == MAIL_STATE_DELETED || m -> deliver_time > time ( NULL ))
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_MONEY_TAKEN , MAIL_ERR_INTERNAL_ERROR );
2010-09-04 13:46:01 -07:00
return ;
}
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_MONEY_TAKEN , MAIL_OK );
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
player -> ModifyMoney ( m -> money );
2010-09-04 13:46:01 -07:00
m -> money = 0 ;
m -> state = MAIL_STATE_CHANGED ;
2011-11-07 11:18:00 -06:00
player -> m_mailsUpdated = true ;
2010-09-04 13:46:01 -07:00
// save money and mail to prevent cheating
SQLTransaction trans = CharacterDatabase . BeginTransaction ();
2011-11-07 11:18:00 -06:00
player -> SaveGoldToDB ( trans );
player -> _SaveMail ( trans );
2010-09-04 13:46:01 -07:00
CharacterDatabase . CommitTransaction ( trans );
}
//called when player lists his received mails
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleGetMailList ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
2010-09-04 13:46:01 -07:00
//load players mails, and mailed items
2011-11-07 11:18:00 -06:00
if ( ! player -> m_mailsLoaded )
2012-03-29 13:42:04 +08:00
player -> _LoadMail ();
2010-09-04 13:46:01 -07:00
// client can't work with packets > max int16 value
const uint32 maxPacketSize = 32767 ;
uint32 mailsCount = 0 ; // real send to client mails amount
uint32 realCount = 0 ; // real mails amount
WorldPacket data ( SMSG_MAIL_LIST_RESULT , ( 200 )); // guess size
data << uint32 ( 0 ); // real mail's count
data << uint8 ( 0 ); // mail's count
time_t cur_time = time ( NULL );
2011-11-07 11:18:00 -06:00
for ( PlayerMails :: iterator itr = player -> GetMailBegin (); itr != player -> GetMailEnd (); ++ itr )
2010-09-04 13:46:01 -07:00
{
2012-07-19 15:34:21 +02:00
// Only first 50 mails are displayed
if ( mailsCount >= 50 )
2010-09-04 13:46:01 -07:00
{
realCount += 1 ;
continue ;
}
// skip deleted or not delivered (deliver delay not expired) mails
if (( * itr ) -> state == MAIL_STATE_DELETED || cur_time < ( * itr ) -> deliver_time )
continue ;
uint8 item_count = ( * itr ) -> items . size (); // max count is MAX_MAIL_ITEMS (12)
2012-07-19 15:34:21 +02:00
size_t next_mail_size = 2 + 4 + 1 + (( * itr ) -> messageType == MAIL_NORMAL ? 8 : 4 ) + 4 * 8 + (( * itr ) -> subject . size () + 1 ) + (( * itr ) -> body . size () + 1 ) + 1 + item_count * ( 1 + 4 + 4 + MAX_INSPECTED_ENCHANTMENT_SLOT * 3 * 4 + 4 + 4 + 4 + 4 + 4 + 4 + 1 );
2010-09-04 13:46:01 -07:00
if ( data . wpos () + next_mail_size > maxPacketSize )
{
realCount += 1 ;
continue ;
}
data << uint16 ( next_mail_size ); // Message size
data << uint32 (( * itr ) -> messageID ); // Message ID
data << uint8 (( * itr ) -> messageType ); // Message Type
2011-09-29 12:43:05 +02:00
switch (( * itr ) -> messageType )
2010-09-04 13:46:01 -07:00
{
case MAIL_NORMAL : // sender guid
data << uint64 ( MAKE_NEW_GUID (( * itr ) -> sender , 0 , HIGHGUID_PLAYER ));
break ;
case MAIL_CREATURE :
case MAIL_GAMEOBJECT :
case MAIL_AUCTION :
2012-11-18 22:42:28 +00:00
case MAIL_CALENDAR :
data << uint32 (( * itr ) -> sender ); // creature/gameobject entry, auction id, calendar event id?
2010-09-04 13:46:01 -07:00
break ;
}
data << uint32 (( * itr ) -> COD ); // COD
2012-08-02 08:04:12 +02:00
data << uint32 ( 0 ); // package (Package.dbc)
2010-09-04 13:46:01 -07:00
data << uint32 (( * itr ) -> stationery ); // stationery (Stationery.dbc)
data << uint32 (( * itr ) -> money ); // Gold
2011-11-26 01:53:44 +01:00
data << uint32 (( * itr ) -> checked ); // flags
2012-12-05 11:39:45 +01:00
data << float ( float (( * itr ) -> expire_time - time ( NULL )) / DAY ); // Time
2010-09-04 13:46:01 -07:00
data << uint32 (( * itr ) -> mailTemplateId ); // mail template (MailTemplate.dbc)
2011-11-26 01:53:44 +01:00
data << ( * itr ) -> subject ; // Subject string - once 00, when mail type = 3, max 256
data << ( * itr ) -> body ; // message? max 8000
2010-09-04 13:46:01 -07:00
data << uint8 ( item_count ); // client limit is 0x10
for ( uint8 i = 0 ; i < item_count ; ++ i )
{
2011-11-07 11:18:00 -06:00
Item * item = player -> GetMItem (( * itr ) -> items [ i ]. item_guid );
2010-09-04 13:46:01 -07:00
// item index (0-6?)
data << uint8 ( i );
// item guid low?
data << uint32 (( item ? item -> GetGUIDLow () : 0 ));
// entry
data << uint32 (( item ? item -> GetEntry () : 0 ));
for ( uint8 j = 0 ; j < MAX_INSPECTED_ENCHANTMENT_SLOT ; ++ j )
{
data << uint32 (( item ? item -> GetEnchantmentId (( EnchantmentSlot ) j ) : 0 ));
data << uint32 (( item ? item -> GetEnchantmentDuration (( EnchantmentSlot ) j ) : 0 ));
data << uint32 (( item ? item -> GetEnchantmentCharges (( EnchantmentSlot ) j ) : 0 ));
}
// can be negative
data << int32 (( item ? item -> GetItemRandomPropertyId () : 0 ));
// unk
data << uint32 (( item ? item -> GetItemSuffixFactor () : 0 ));
// stack count
data << uint32 (( item ? item -> GetCount () : 0 ));
// charges
data << uint32 (( item ? item -> GetSpellCharges () : 0 ));
// durability
data << uint32 (( item ? item -> GetUInt32Value ( ITEM_FIELD_MAXDURABILITY ) : 0 ));
// durability
data << uint32 (( item ? item -> GetUInt32Value ( ITEM_FIELD_DURABILITY ) : 0 ));
// unknown wotlk
data << uint8 ( 0 );
}
2012-07-19 15:34:21 +02:00
++ realCount ;
++ mailsCount ;
2010-09-04 13:46:01 -07:00
}
data . put < uint32 > ( 0 , realCount ); // this will display warning about undelivered mail to player if realCount > mailsCount
data . put < uint8 > ( 4 , mailsCount ); // set real send mails to client
SendPacket ( & data );
// recalculate m_nextMailDelivereTime and unReadMails
_player -> UpdateNextMailTimeAndUnreads ();
}
//used when player copies mail body to his inventory
2012-11-27 13:02:04 +01:00
void WorldSession :: HandleMailCreateTextItem ( WorldPacket & recvData )
2010-09-04 13:46:01 -07:00
{
uint64 mailbox ;
uint32 mailId ;
2012-11-02 22:23:14 +01:00
recvData >> mailbox ;
recvData >> mailId ;
2010-09-04 13:46:01 -07:00
if ( ! GetPlayer () -> GetGameObjectIfCanInteractWith ( mailbox , GAMEOBJECT_TYPE_MAILBOX ))
return ;
2011-11-07 11:18:00 -06:00
Player * player = _player ;
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
Mail * m = player -> GetMail ( mailId );
2010-09-04 13:46:01 -07:00
if ( ! m || ( m -> body . empty () && ! m -> mailTemplateId ) || m -> state == MAIL_STATE_DELETED || m -> deliver_time > time ( NULL ))
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_MADE_PERMANENT , MAIL_ERR_INTERNAL_ERROR );
2010-09-04 13:46:01 -07:00
return ;
}
2011-09-15 14:08:17 +02:00
Item * bodyItem = new Item ; // This is not bag and then can be used new Item.
2011-11-07 11:18:00 -06:00
if ( ! bodyItem -> Create ( sObjectMgr -> GenerateLowGuid ( HIGHGUID_ITEM ), MAIL_BODY_ITEM_TEMPLATE , player ))
2010-09-04 13:46:01 -07:00
{
delete bodyItem ;
return ;
}
// in mail template case we need create new item text
if ( m -> mailTemplateId )
{
MailTemplateEntry const * mailTemplateEntry = sMailTemplateStore . LookupEntry ( m -> mailTemplateId );
if ( ! mailTemplateEntry )
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_MADE_PERMANENT , MAIL_ERR_INTERNAL_ERROR );
2010-09-04 13:46:01 -07:00
return ;
}
bodyItem -> SetText ( mailTemplateEntry -> content [ GetSessionDbcLocale ()]);
}
else
bodyItem -> SetText ( m -> body );
bodyItem -> SetUInt32Value ( ITEM_FIELD_CREATOR , m -> sender );
bodyItem -> SetFlag ( ITEM_FIELD_FLAGS , ITEM_FLAG_MAIL_TEXT_MASK );
2012-08-03 14:20:18 +02:00
sLog -> outInfo ( LOG_FILTER_NETWORKIO , "HandleMailCreateTextItem mailid=%u" , mailId );
2010-09-04 13:46:01 -07:00
ItemPosCountVec dest ;
uint8 msg = _player -> CanStoreItem ( NULL_BAG , NULL_SLOT , dest , bodyItem , false );
if ( msg == EQUIP_ERR_OK )
{
m -> checked = m -> checked | MAIL_CHECK_MASK_COPIED ;
m -> state = MAIL_STATE_CHANGED ;
2011-11-07 11:18:00 -06:00
player -> m_mailsUpdated = true ;
2010-09-04 13:46:01 -07:00
2011-11-07 11:18:00 -06:00
player -> StoreItem ( dest , bodyItem , true );
player -> SendMailResult ( mailId , MAIL_MADE_PERMANENT , MAIL_OK );
2010-09-04 13:46:01 -07:00
}
else
{
2011-11-07 11:18:00 -06:00
player -> SendMailResult ( mailId , MAIL_MADE_PERMANENT , MAIL_ERR_EQUIP_ERROR , msg );
2010-09-04 13:46:01 -07:00
delete bodyItem ;
}
}
//TODO Fix me! ... this void has probably bad condition, but good data are sent
2012-11-02 22:23:14 +01:00
void WorldSession :: HandleQueryNextMailTime ( WorldPacket & /*recvData*/ )
2010-09-04 13:46:01 -07:00
{
WorldPacket data ( MSG_QUERY_NEXT_MAIL_TIME , 8 );
if ( ! _player -> m_mailsLoaded )
_player -> _LoadMail ();
if ( _player -> unReadMails > 0 )
{
2012-07-19 15:34:21 +02:00
data << float ( 0 ); // float
2010-09-04 13:46:01 -07:00
data << uint32 ( 0 ); // count
uint32 count = 0 ;
time_t now = time ( NULL );
2012-07-19 15:34:21 +02:00
std :: set < uint32 > sentSenders ;
2010-09-04 13:46:01 -07:00
for ( PlayerMails :: iterator itr = _player -> GetMailBegin (); itr != _player -> GetMailEnd (); ++ itr )
{
2011-09-15 14:08:17 +02:00
Mail * m = ( * itr );
2010-09-04 13:46:01 -07:00
// must be not checked yet
if ( m -> checked & MAIL_CHECK_MASK_READ )
continue ;
// and already delivered
if ( now < m -> deliver_time )
continue ;
2012-07-19 15:34:21 +02:00
// only send each mail sender once
if ( sentSenders . count ( m -> sender ))
continue ;
2010-09-04 13:46:01 -07:00
2012-07-19 15:34:21 +02:00
data << uint64 ( m -> messageType == MAIL_NORMAL ? m -> sender : 0 ); // player guid
data << uint32 ( m -> messageType != MAIL_NORMAL ? m -> sender : 0 ); // non-player entries
data << uint32 ( m -> messageType );
data << uint32 ( m -> stationery );
data << float ( m -> deliver_time - now );
2010-09-04 13:46:01 -07:00
2012-07-19 15:34:21 +02:00
sentSenders . insert ( m -> sender );
2010-09-04 13:46:01 -07:00
++ count ;
if ( count == 2 ) // do not display more than 2 mails
break ;
}
2012-07-19 15:34:21 +02:00
2010-09-04 13:46:01 -07:00
data . put < uint32 > ( 4 , count );
}
else
{
2012-07-19 15:34:21 +02:00
data << float ( - DAY );
data << uint32 ( 0 );
2010-09-04 13:46:01 -07:00
}
2012-07-19 15:34:21 +02:00
2010-09-04 13:46:01 -07:00
SendPacket ( & data );
2011-04-28 22:42:33 +02:00
}