Merge branch 'master' into 310

Conflicts:
	src/game/Player.cpp
This commit is contained in:
tomrus88 2009-05-28 10:16:02 +04:00
commit f6e2b55e2c
25 changed files with 382 additions and 240 deletions

View file

@ -21,7 +21,7 @@
DROP TABLE IF EXISTS `character_db_version`; DROP TABLE IF EXISTS `character_db_version`;
CREATE TABLE `character_db_version` ( CREATE TABLE `character_db_version` (
`required_7887_01_characters_character_pet` bit(1) default NULL `required_7903_01_characters_character_pet` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB';
-- --
@ -472,7 +472,6 @@ CREATE TABLE `character_pet` (
`level` int(11) unsigned NOT NULL default '1', `level` int(11) unsigned NOT NULL default '1',
`exp` int(11) unsigned NOT NULL default '0', `exp` int(11) unsigned NOT NULL default '0',
`Reactstate` tinyint(1) unsigned NOT NULL default '0', `Reactstate` tinyint(1) unsigned NOT NULL default '0',
`talentpoints` int(11) unsigned NOT NULL default '0',
`name` varchar(100) default 'Pet', `name` varchar(100) default 'Pet',
`renamed` tinyint(1) unsigned NOT NULL default '0', `renamed` tinyint(1) unsigned NOT NULL default '0',
`slot` int(11) unsigned NOT NULL default '0', `slot` int(11) unsigned NOT NULL default '0',

View file

@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `db_version`;
CREATE TABLE `db_version` ( 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,
`required_7893_01_mangos_command` bit(1) default NULL `required_7902_02_mangos_pool_gameobject` 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';
-- --
@ -820,8 +820,8 @@ CREATE TABLE `creature_template` (
`family` tinyint(4) NOT NULL default '0', `family` tinyint(4) NOT NULL default '0',
`trainer_type` tinyint(4) NOT NULL default '0', `trainer_type` tinyint(4) NOT NULL default '0',
`trainer_spell` mediumint(8) unsigned NOT NULL default '0', `trainer_spell` mediumint(8) unsigned NOT NULL default '0',
`class` tinyint(3) unsigned NOT NULL default '0', `trainer_class` tinyint(3) unsigned NOT NULL default '0',
`race` tinyint(3) unsigned NOT NULL default '0', `trainer_race` tinyint(3) unsigned NOT NULL default '0',
`minrangedmg` float NOT NULL default '0', `minrangedmg` float NOT NULL default '0',
`maxrangedmg` float NOT NULL default '0', `maxrangedmg` float NOT NULL default '0',
`rangedattackpower` smallint(5) unsigned NOT NULL default '0', `rangedattackpower` smallint(5) unsigned NOT NULL default '0',
@ -12938,7 +12938,8 @@ CREATE TABLE `pool_creature` (
`pool_entry` mediumint(8) unsigned NOT NULL default '0', `pool_entry` mediumint(8) unsigned NOT NULL default '0',
`chance` float unsigned NOT NULL default '0', `chance` float unsigned NOT NULL default '0',
`description` varchar(255) NOT NULL, `description` varchar(255) NOT NULL,
PRIMARY KEY (`pool_entry`,`guid`) PRIMARY KEY (`pool_entry`,`guid`),
INDEX `idx_guid`(`guid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@ -12961,7 +12962,8 @@ CREATE TABLE `pool_gameobject` (
`pool_entry` mediumint(8) unsigned NOT NULL default '0', `pool_entry` mediumint(8) unsigned NOT NULL default '0',
`chance` float unsigned NOT NULL default '0', `chance` float unsigned NOT NULL default '0',
`description` varchar(255) NOT NULL, `description` varchar(255) NOT NULL,
PRIMARY KEY (`guid`,`pool_entry`) PRIMARY KEY (`guid`,`pool_entry`),
INDEX `idx_guid`(`guid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --

View file

@ -0,0 +1,4 @@
ALTER TABLE db_version CHANGE COLUMN required_7893_01_mangos_command required_7896_01_mangos_creature_template bit;
ALTER TABLE creature_template CHANGE COLUMN class trainer_class tinyint(3) unsigned NOT NULL default '0';
ALTER TABLE creature_template CHANGE COLUMN race trainer_race tinyint(3) unsigned NOT NULL default '0';

View file

@ -0,0 +1,4 @@
ALTER TABLE db_version CHANGE COLUMN required_7896_01_mangos_creature_template required_7902_01_mangos_pool_creature bit;
ALTER TABLE `pool_creature`
ADD INDEX `idx_guid`(`guid`);

View file

@ -0,0 +1,4 @@
ALTER TABLE db_version CHANGE COLUMN required_7902_01_mangos_pool_creature required_7902_02_mangos_pool_gameobject bit;
ALTER TABLE `pool_gameobject`
ADD INDEX `idx_guid`(`guid`);

View file

@ -0,0 +1,4 @@
ALTER TABLE character_db_version CHANGE COLUMN required_7887_01_characters_character_pet required_7903_01_characters_character_pet bit;
ALTER TABLE `character_pet`
DROP `talentpoints`;

View file

@ -200,6 +200,10 @@ pkgdata_DATA = \
7886_01_mangos_petcreateinfo_spell.sql \ 7886_01_mangos_petcreateinfo_spell.sql \
7887_01_characters_character_pet.sql \ 7887_01_characters_character_pet.sql \
7893_01_mangos_command.sql \ 7893_01_mangos_command.sql \
7896_01_mangos_creature_template.sql \
7902_01_mangos_pool_creature.sql \
7902_02_mangos_pool_gameobject.sql \
7903_01_characters_character_pet.sql \
README README
## Additional files to include when running 'make dist' ## Additional files to include when running 'make dist'
@ -380,4 +384,8 @@ EXTRA_DIST = \
7886_01_mangos_petcreateinfo_spell.sql \ 7886_01_mangos_petcreateinfo_spell.sql \
7887_01_characters_character_pet.sql \ 7887_01_characters_character_pet.sql \
7893_01_mangos_command.sql \ 7893_01_mangos_command.sql \
7896_01_mangos_creature_template.sql \
7902_01_mangos_pool_creature.sql \
7902_02_mangos_pool_gameobject.sql \
7903_01_characters_character_pet.sql \
README README

View file

@ -605,12 +605,12 @@ bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
switch(GetCreatureInfo()->trainer_type) switch(GetCreatureInfo()->trainer_type)
{ {
case TRAINER_TYPE_CLASS: case TRAINER_TYPE_CLASS:
if(pPlayer->getClass()!=GetCreatureInfo()->classNum) if(pPlayer->getClass()!=GetCreatureInfo()->trainer_class)
{ {
if(msg) if(msg)
{ {
pPlayer->PlayerTalkClass->ClearMenus(); pPlayer->PlayerTalkClass->ClearMenus();
switch(GetCreatureInfo()->classNum) switch(GetCreatureInfo()->trainer_class)
{ {
case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break; case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break;
case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break; case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break;
@ -635,12 +635,12 @@ bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
} }
break; break;
case TRAINER_TYPE_MOUNTS: case TRAINER_TYPE_MOUNTS:
if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race) if(GetCreatureInfo()->trainer_race && pPlayer->getRace() != GetCreatureInfo()->trainer_race)
{ {
if(msg) if(msg)
{ {
pPlayer->PlayerTalkClass->ClearMenus(); pPlayer->PlayerTalkClass->ClearMenus();
switch(GetCreatureInfo()->classNum) switch(GetCreatureInfo()->trainer_class)
{ {
case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break; case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break;
case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break; case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break;
@ -710,7 +710,7 @@ bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
{ {
return pPlayer->getLevel() >= 10 return pPlayer->getLevel() >= 10
&& GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
&& pPlayer->getClass() == GetCreatureInfo()->classNum; && pPlayer->getClass() == GetCreatureInfo()->trainer_class;
} }
void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid ) void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
@ -771,7 +771,7 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
cantalking=false; cantalking=false;
break; break;
case GOSSIP_OPTION_UNLEARNPETSKILLS: case GOSSIP_OPTION_UNLEARNPETSKILLS:
if(!pPlayer->GetPet() || pPlayer->GetPet()->getPetType() != HUNTER_PET || pPlayer->GetPet()->m_spells.size() <= 1 || GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo()->classNum != CLASS_HUNTER) if(!pPlayer->GetPet() || pPlayer->GetPet()->getPetType() != HUNTER_PET || pPlayer->GetPet()->m_spells.size() <= 1 || GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo()->trainer_class != CLASS_HUNTER)
cantalking=false; cantalking=false;
break; break;
case GOSSIP_OPTION_TAXIVENDOR: case GOSSIP_OPTION_TAXIVENDOR:

View file

@ -190,8 +190,8 @@ struct CreatureInfo
uint32 family; // enum CreatureFamily values (optional) uint32 family; // enum CreatureFamily values (optional)
uint32 trainer_type; uint32 trainer_type;
uint32 trainer_spell; uint32 trainer_spell;
uint32 classNum; uint32 trainer_class;
uint32 race; uint32 trainer_race;
float minrangedmg; float minrangedmg;
float maxrangedmg; float maxrangedmg;
uint32 rangedattackpower; uint32 rangedattackpower;

View file

@ -696,18 +696,19 @@ bool Item::IsEquipped() const
return !IsInBag() && m_slot < EQUIPMENT_SLOT_END; return !IsInBag() && m_slot < EQUIPMENT_SLOT_END;
} }
bool Item::CanBeTraded() const bool Item::CanBeTraded(bool mail) const
{ {
if(IsSoulBound()) if ((!mail || !IsBoundAccountWide()) && IsSoulBound())
return false;
if(IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()) )
return false; return false;
if(Player* owner = GetOwner()) if (IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()) )
return false;
if (Player* owner = GetOwner())
{ {
if(owner->CanUnequipItem(GetPos(),false) != EQUIP_ERR_OK ) if (owner->CanUnequipItem(GetPos(),false) != EQUIP_ERR_OK )
return false; return false;
if(owner->GetLootGUID()==GetGUID()) if (owner->GetLootGUID()==GetGUID())
return false; return false;
} }
@ -941,3 +942,29 @@ Item* Item::CloneItem( uint32 count, Player const* player ) const
newItem->SetItemRandomProperties(GetItemRandomPropertyId()); newItem->SetItemRandomProperties(GetItemRandomPropertyId());
return newItem; return newItem;
} }
bool Item::IsBindedNotWith( Player const* player ) const
{
// not binded item
if(!IsSoulBound())
return false;
// own item
if(GetOwnerGUID()== player->GetGUID())
return false;
// not BOA item case
if(!IsBoundAccountWide())
return true;
// online
if(Player* owner = objmgr.GetPlayer(GetOwnerGUID()))
{
return owner->GetSession()->GetAccountId() != player->GetSession()->GetAccountId();
}
// offline slow case
else
{
return objmgr.GetPlayerAccountIdByGUID(GetOwnerGUID()) != player->GetSession()->GetAccountId();
}
}

View file

@ -116,8 +116,11 @@ enum InventoryChangeFailure
EQUIP_ERR_TOO_MUCH_GOLD = 77, EQUIP_ERR_TOO_MUCH_GOLD = 77,
EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78, EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78,
EQUIP_ERR_CANNOT_TRADE_THAT = 79, EQUIP_ERR_CANNOT_TRADE_THAT = 79,
EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80 EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80,
// probably exist more // no output = 81,
EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS = 82,
// no output = 83,
// crash client = 84,
}; };
enum BuyFailure enum BuyFailure
@ -212,8 +215,8 @@ class MANGOS_DLL_SPEC Item : public Object
void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED,val); } void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED,val); }
bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); } bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); }
bool IsAccountBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); } bool IsBoundAccountWide() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); }
bool IsBindedNotWith(uint64 guid) const { return IsSoulBound() && GetOwnerGUID()!= guid; } bool IsBindedNotWith(Player const* player) const;
bool IsBoundByEnchant() const; bool IsBoundByEnchant() const;
virtual void SaveToDB(); virtual void SaveToDB();
virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result = NULL); virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result = NULL);
@ -222,7 +225,7 @@ class MANGOS_DLL_SPEC Item : public Object
bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; } bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; }
bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; } bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; }
bool CanBeTraded() const; bool CanBeTraded(bool mail = false) const;
void SetInTrade(bool b = true) { mb_in_trade = b; } void SetInTrade(bool b = true) { mb_in_trade = b; }
bool IsInTrade() const { return mb_in_trade; } bool IsInTrade() const { return mb_in_trade; }

View file

@ -101,7 +101,7 @@ enum ItemBondingType
// masks for ITEM_FIELD_FLAGS field // masks for ITEM_FIELD_FLAGS field
enum ITEM_FLAGS enum ITEM_FLAGS
{ {
ITEM_FLAGS_BINDED = 0x00000001, ITEM_FLAGS_BINDED = 0x00000001, // set in game at binding, not set in template
ITEM_FLAGS_CONJURED = 0x00000002, ITEM_FLAGS_CONJURED = 0x00000002,
ITEM_FLAGS_OPENABLE = 0x00000004, ITEM_FLAGS_OPENABLE = 0x00000004,
ITEM_FLAGS_WRAPPED = 0x00000008, ITEM_FLAGS_WRAPPED = 0x00000008,
@ -114,7 +114,7 @@ enum ITEM_FLAGS
ITEM_FLAGS_USEABLE_IN_ARENA = 0x00200000, ITEM_FLAGS_USEABLE_IN_ARENA = 0x00200000,
ITEM_FLAGS_THROWABLE = 0x00400000, // not used in game for check trow possibility, only for item in game tooltip ITEM_FLAGS_THROWABLE = 0x00400000, // not used in game for check trow possibility, only for item in game tooltip
ITEM_FLAGS_SPECIALUSE = 0x00800000, // last used flag in 2.3.0 ITEM_FLAGS_SPECIALUSE = 0x00800000, // last used flag in 2.3.0
ITEM_FLAGS_BOA = 0x08000000, // bind on account ITEM_FLAGS_BOA = 0x08000000, // bind on account (set in template for items that can binded in like way)
ITEM_FLAGS_MILLABLE = 0x20000000 ITEM_FLAGS_MILLABLE = 0x20000000
}; };

View file

@ -77,7 +77,10 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
recv_data >> items_count; // attached items count recv_data >> items_count; // attached items count
if(items_count > 12) // client limit if(items_count > 12) // client limit
{
GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
return; return;
}
// recheck // recheck
CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+items_count*(1+8)+4+4+8+1); CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+items_count*(1+8)+4+4+8+1);
@ -113,7 +116,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
{ {
sLog.outDetail("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", sLog.outDetail("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",
pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
pl->SendMailResult(0, 0, MAIL_ERR_RECIPIENT_NOT_FOUND); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND);
return; return;
} }
@ -121,7 +124,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
if(pl->GetGUID() == rc) if(pl->GetGUID() == rc)
{ {
pl->SendMailResult(0, 0, MAIL_ERR_CANNOT_SEND_TO_SELF); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
return; return;
} }
@ -131,7 +134,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
if (pl->GetMoney() < reqmoney) if (pl->GetMoney() < reqmoney)
{ {
pl->SendMailResult(0, 0, MAIL_ERR_NOT_ENOUGH_MONEY); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY);
return; return;
} }
@ -159,16 +162,22 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
//do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. //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) if (mails_count > 100)
{ {
pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED);
return; return;
} }
// test the receiver's Faction... // test the receiver's Faction...
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER)
{ {
pl->SendMailResult(0, 0, MAIL_ERR_NOT_YOUR_TEAM); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM);
return; return;
} }
uint32 rc_account = 0;
if(receive)
rc_account = receive->GetSession()->GetAccountId();
else
rc_account = objmgr.GetPlayerAccountIdByGUID(rc);
if (items_count) if (items_count)
{ {
for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
@ -177,31 +186,44 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
if(!mailItem.item_guidlow) if(!mailItem.item_guidlow)
{ {
pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return; return;
} }
mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM)); 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) // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if(!mailItem.item || !mailItem.item->CanBeTraded()) if(!mailItem.item)
{ {
pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return; 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)) if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION))
{ {
pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return; return;
} }
if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
{ {
pl->SendMailResult(0, 0, MAIL_ERR_CANT_SEND_WRAPPED_COD); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
return; return;
} }
} }
} }
pl->SendMailResult(0, 0, MAIL_OK); pl->SendMailResult(0, MAIL_SEND, MAIL_OK);
uint32 itemTextId = 0; uint32 itemTextId = 0;
if (!body.empty()) if (!body.empty())
@ -216,12 +238,6 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
if(items_count > 0 || money > 0) if(items_count > 0 || money > 0)
{ {
uint32 rc_account = 0;
if(receive)
rc_account = receive->GetSession()->GetAccountId();
else
rc_account = objmgr.GetPlayerAccountIdByGUID(rc);
if (items_count > 0) if (items_count > 0)
{ {
for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
@ -313,7 +329,7 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data )
Mail *m = pl->GetMail(mailId); Mail *m = pl->GetMail(mailId);
if(m) if(m)
m->state = MAIL_STATE_DELETED; m->state = MAIL_STATE_DELETED;
pl->SendMailResult(mailId, MAIL_DELETED, 0); pl->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
} }
void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
@ -365,7 +381,7 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId); SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId);
delete m; //we can deallocate old mail delete m; //we can deallocate old mail
pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, 0); 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 ) 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 )
@ -508,7 +524,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count);
} }
else else
pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_BAG_FULL, msg); pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg);
} }
void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data ) void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data )
@ -532,7 +548,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data )
return; return;
} }
pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, 0); pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
pl->ModifyMoney(m->money); pl->ModifyMoney(m->money);
m->money = 0; m->money = 0;
@ -725,11 +741,11 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
pl->StoreItem(dest, bodyItem, true); pl->StoreItem(dest, bodyItem, true);
//bodyItem->SetState(ITEM_NEW, pl); is set automatically //bodyItem->SetState(ITEM_NEW, pl); is set automatically
pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, 0); pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK);
} }
else else
{ {
pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_BAG_FULL, msg); pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_EQUIP_ERROR, msg);
delete bodyItem; delete bodyItem;
} }
} }

View file

@ -26,30 +26,6 @@ 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 MAIL_RESPONSE
{
MAIL_OK = 0,
MAIL_MONEY_TAKEN = 1,
MAIL_ITEM_TAKEN = 2,
MAIL_RETURNED_TO_SENDER = 3,
MAIL_DELETED = 4,
MAIL_MADE_PERMANENT = 5
};
enum MAIL_ERRORS
{
MAIL_ERR_BAG_FULL = 1,
MAIL_ERR_CANNOT_SEND_TO_SELF = 2,
MAIL_ERR_NOT_ENOUGH_MONEY = 3,
MAIL_ERR_RECIPIENT_NOT_FOUND = 4,
MAIL_ERR_NOT_YOUR_TEAM = 5,
MAIL_ERR_INTERNAL_ERROR = 6,
MAIL_ERR_DISABLED_FOR_TRIAL_ACC = 14,
MAIL_ERR_RECIPIENT_CAP_REACHED = 15,
MAIL_ERR_CANT_SEND_WRAPPED_COD = 16,
MAIL_ERR_MAIL_AND_CHAT_SUSPENDED = 17
};
enum MailCheckMask enum MailCheckMask
{ {
MAIL_CHECK_MASK_NONE = 0, MAIL_CHECK_MASK_NONE = 0,

View file

@ -497,15 +497,15 @@ void ObjectMgr::LoadCreatureTemplates()
continue; continue;
} }
if(cInfo->classNum != heroicInfo->classNum) if(cInfo->trainer_class != heroicInfo->trainer_class)
{ {
sLog.outErrorDb("Creature (Entry: %u) has different `classNum` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry); sLog.outErrorDb("Creature (Entry: %u) has different `trainer_class` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
continue; continue;
} }
if(cInfo->race != heroicInfo->race) if(cInfo->trainer_race != heroicInfo->trainer_race)
{ {
sLog.outErrorDb("Creature (Entry: %u) has different `race` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry); sLog.outErrorDb("Creature (Entry: %u) has different `trainer_race` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
continue; continue;
} }

View file

@ -87,25 +87,25 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
QueryResult *result; QueryResult *result;
if (petnumber) if (petnumber)
// known petnumber entry 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // known petnumber entry 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
"FROM character_pet WHERE owner = '%u' AND id = '%u'", "FROM character_pet WHERE owner = '%u' AND id = '%u'",
ownerid, petnumber); ownerid, petnumber);
else if (current) else if (current)
// current pet (slot 0) 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // current pet (slot 0) 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
"FROM character_pet WHERE owner = '%u' AND slot = '%u'", "FROM character_pet WHERE owner = '%u' AND slot = '%u'",
ownerid, PET_SAVE_AS_CURRENT ); ownerid, PET_SAVE_AS_CURRENT );
else if (petentry) else if (petentry)
// known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
// 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
"FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '%u' OR slot > '%u') ", "FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '%u' OR slot > '%u') ",
ownerid, petentry,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); ownerid, petentry,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT);
else else
// any current or other non-stabled pet (for hunter "call pet") // any current or other non-stabled pet (for hunter "call pet")
// 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
"FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') ", "FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') ",
ownerid,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); ownerid,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT);
@ -122,7 +122,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
return false; return false;
} }
uint32 summon_spell_id = fields[18].GetUInt32(); uint32 summon_spell_id = fields[17].GetUInt32();
SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id); SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id);
bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0; bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0;
@ -164,7 +164,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
return false; return false;
} }
setPetType(PetType(fields[19].GetUInt8())); setPetType(PetType(fields[18].GetUInt8()));
SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, owner->getFaction()); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, owner->getFaction());
SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id); SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
@ -184,7 +184,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
SetNativeDisplayId(fields[3].GetUInt32()); SetNativeDisplayId(fields[3].GetUInt32());
uint32 petlevel = fields[4].GetUInt32(); uint32 petlevel = fields[4].GetUInt32();
SetUInt32Value(UNIT_NPC_FLAGS, 0); SetUInt32Value(UNIT_NPC_FLAGS, 0);
SetName(fields[9].GetString()); SetName(fields[8].GetString());
switch (getPetType()) switch (getPetType())
{ {
@ -197,14 +197,13 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
break; break;
case HUNTER_PET: case HUNTER_PET:
SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[7].GetUInt32());
SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE); SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
SetByteValue(UNIT_FIELD_BYTES_2, 2, fields[10].GetBool() ? UNIT_RENAME_NOT_ALLOWED : UNIT_RENAME_ALLOWED); SetByteValue(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_RENAME_NOT_ALLOWED : UNIT_RENAME_ALLOWED);
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet abandon, cancel) // this enables popup window (pet abandon, cancel)
SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS)); SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS));
SetPower(POWER_HAPPINESS, fields[13].GetUInt32()); SetPower(POWER_HAPPINESS, fields[12].GetUInt32());
setPowerType(POWER_FOCUS); setPowerType(POWER_FOCUS);
break; break;
default: default:
@ -215,20 +214,22 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
SetPvP(true); SetPvP(true);
InitStatsForLevel(petlevel); InitStatsForLevel(petlevel);
InitTalentForLevel(); // set original talents points before spell loading
SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32()); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
SetCreatorGUID(owner->GetGUID()); SetCreatorGUID(owner->GetGUID());
m_charmInfo->SetReactState(ReactStates(fields[6].GetUInt8())); m_charmInfo->SetReactState(ReactStates(fields[6].GetUInt8()));
uint32 savedhealth = fields[11].GetUInt32(); uint32 savedhealth = fields[10].GetUInt32();
uint32 savedmana = fields[12].GetUInt32(); uint32 savedmana = fields[11].GetUInt32();
// set current pet as current // set current pet as current
// 0=current // 0=current
// 1..MAX_PET_STABLES in stable slot // 1..MAX_PET_STABLES in stable slot
// PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning))
if (fields[8].GetUInt32() != 0) if (fields[7].GetUInt32() != 0)
{ {
CharacterDatabase.BeginTransaction(); CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u' AND id <> '%u'", CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u' AND id <> '%u'",
@ -240,30 +241,18 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
if (!is_temporary_summoned) if (!is_temporary_summoned)
{ {
// permanent controlled pets store state in DB if(!m_charmInfo->LoadActionBar(fields[13].GetCppString()))
Tokens tokens = StrSplit(fields[14].GetString(), " ");
if (tokens.size() != 20)
{ {
delete result; delete result;
return false; return false;
} }
int index;
Tokens::iterator iter;
for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index )
{
m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str());
++iter;
m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str());
}
} }
// since last save (in seconds) // since last save (in seconds)
uint32 timediff = (time(NULL) - fields[15].GetUInt32()); uint32 timediff = (time(NULL) - fields[14].GetUInt32());
m_resetTalentsCost = fields[16].GetUInt32(); m_resetTalentsCost = fields[15].GetUInt32();
m_resetTalentsTime = fields[17].GetUInt64(); m_resetTalentsTime = fields[16].GetUInt64();
delete result; delete result;
@ -299,6 +288,10 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
// Spells should be loaded after pet is added to map, because in CheckCast is check on it // Spells should be loaded after pet is added to map, because in CheckCast is check on it
_LoadSpells(); _LoadSpells();
InitLevelupSpellsForLevel();
CleanupActionBar(); // remove unknown spells from action bar after load
_LoadSpellCooldowns(); _LoadSpellCooldowns();
owner->SetPet(this); // in DB stored only full controlled creature owner->SetPet(this); // in DB stored only full controlled creature
@ -329,8 +322,6 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
} }
} }
InitLevelupSpellsForLevel();
m_loading = false; m_loading = false;
SynchronizeLevelWithOwner(); SynchronizeLevelWithOwner();
@ -404,7 +395,7 @@ void Pet::SavePetToDB(PetSaveMode mode)
owner,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); owner,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT);
// save pet // save pet
std::ostringstream ss; std::ostringstream ss;
ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) " ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) "
<< "VALUES (" << "VALUES ("
<< m_charmInfo->GetPetNumber() << ", " << m_charmInfo->GetPetNumber() << ", "
<< GetEntry() << ", " << GetEntry() << ", "
@ -413,7 +404,6 @@ void Pet::SavePetToDB(PetSaveMode mode)
<< getLevel() << ", " << getLevel() << ", "
<< GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
<< uint32(m_charmInfo->GetReactState()) << ", " << uint32(m_charmInfo->GetReactState()) << ", "
<< uint32(GetFreeTalentPoints()) << ", "
<< uint32(mode) << ", '" << uint32(mode) << ", '"
<< name.c_str() << "', " << name.c_str() << "', "
<< uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", "
@ -421,8 +411,12 @@ void Pet::SavePetToDB(PetSaveMode mode)
<< curmana << ", " << curmana << ", "
<< GetPower(POWER_HAPPINESS) << ", '"; << GetPower(POWER_HAPPINESS) << ", '";
for(uint32 i = 0; i < 10; ++i) for(uint32 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; {
ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " "
<< uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " ";
};
ss << "', " ss << "', "
<< time(NULL) << ", " << time(NULL) << ", "
<< uint32(m_resetTalentsCost) << ", " << uint32(m_resetTalentsCost) << ", "
@ -1099,7 +1093,17 @@ void Pet::_LoadSpells()
{ {
Field *fields = result->Fetch(); Field *fields = result->Fetch();
addSpell(fields[0].GetUInt32(), ActiveStates(fields[1].GetUInt16()), PETSPELL_UNCHANGED); uint32 spell_id = fields[0].GetUInt32();
// load only pet talents, other spell types auto-learned
if(GetTalentSpellCost(spell_id)==0)
{
CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
sLog.outError("Table `pet_spell` have non-talent spell %u , spell removed from table for all pets.",spell_id);
continue;
}
addSpell(spell_id, ActiveStates(fields[1].GetUInt16()), PETSPELL_UNCHANGED,PETSPELL_TALENT);
} }
while( result->NextRow() ); while( result->NextRow() );
@ -1113,8 +1117,8 @@ void Pet::_SaveSpells()
{ {
++next; ++next;
// prevent saving family passives to DB // save only talent spells for pets, other spells auto-applied
if (itr->second.type == PETSPELL_FAMILY) if (itr->second.type != PETSPELL_TALENT)
continue; continue;
switch(itr->second.state) switch(itr->second.state)
@ -1325,6 +1329,9 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
// talent: unlearn all other talent ranks (high and low) // talent: unlearn all other talent ranks (high and low)
if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id)) if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
{ {
// propertly mark spell for allow save
newspell.type = PETSPELL_TALENT;
if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id )) if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
{ {
for(int i=0; i < MAX_TALENT_RANK; ++i) for(int i=0; i < MAX_TALENT_RANK; ++i)
@ -1337,7 +1344,7 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
// skip unknown ranks // skip unknown ranks
if(!HasSpell(rankSpellId)) if(!HasSpell(rankSpellId))
continue; continue;
removeSpell(rankSpellId,false); removeSpell(rankSpellId,false,false);
} }
} }
} }
@ -1358,7 +1365,7 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
ToggleAutocast(itr2->first, false); ToggleAutocast(itr2->first, false);
oldspell_id = itr2->first; oldspell_id = itr2->first;
unlearnSpell(itr2->first,false); unlearnSpell(itr2->first,false,false);
break; break;
} }
// ignore new lesser rank // ignore new lesser rank
@ -1373,7 +1380,7 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
if (IsPassiveSpell(spell_id)) if (IsPassiveSpell(spell_id))
CastSpell(this, spell_id, true); CastSpell(this, spell_id, true);
else else
m_charmInfo->AddSpellToAB(oldspell_id, spell_id); m_charmInfo->AddSpellToActionBar(spell_id);
if(newspell.active == ACT_ENABLED) if(newspell.active == ACT_ENABLED)
ToggleAutocast(spell_id, true); ToggleAutocast(spell_id, true);
@ -1396,16 +1403,17 @@ bool Pet::learnSpell(uint32 spell_id)
if (!addSpell(spell_id)) if (!addSpell(spell_id))
return false; return false;
Unit* owner = GetOwner(); if(!m_loading)
if(owner && owner->GetTypeId() == TYPEID_PLAYER)
{ {
if(!m_loading) Unit* owner = GetOwner();
if(owner && owner->GetTypeId() == TYPEID_PLAYER)
{ {
WorldPacket data(SMSG_PET_LEARNED_SPELL, 2); WorldPacket data(SMSG_PET_LEARNED_SPELL, 2);
data << uint16(spell_id); data << uint16(spell_id);
((Player*)owner)->GetSession()->SendPacket(&data); ((Player*)owner)->GetSession()->SendPacket(&data);
((Player*)owner)->PetSpellInitialize();
} }
((Player*)owner)->PetSpellInitialize();
} }
return true; return true;
} }
@ -1441,7 +1449,7 @@ void Pet::InitLevelupSpellsForLevel()
// will called first if level down // will called first if level down
if(spellEntry->spellLevel > level) if(spellEntry->spellLevel > level)
unlearnSpell(spellEntry->Id,false); unlearnSpell(spellEntry->Id,true);
// will called if level up // will called if level up
else else
learnSpell(spellEntry->Id); learnSpell(spellEntry->Id);
@ -1449,9 +1457,9 @@ void Pet::InitLevelupSpellsForLevel()
} }
} }
bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev) bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
{ {
if(removeSpell(spell_id,learn_prev)) if(removeSpell(spell_id,learn_prev,clear_ab))
{ {
if(GetOwner()->GetTypeId() == TYPEID_PLAYER) if(GetOwner()->GetTypeId() == TYPEID_PLAYER)
{ {
@ -1467,7 +1475,7 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev)
return false; return false;
} }
bool Pet::removeSpell(uint32 spell_id, bool learn_prev) bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
{ {
PetSpellMap::iterator itr = m_spells.find(spell_id); PetSpellMap::iterator itr = m_spells.find(spell_id);
if (itr == m_spells.end()) if (itr == m_spells.end())
@ -1498,29 +1506,35 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev)
if (learn_prev) if (learn_prev)
{ {
if (uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id)) if (uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id))
{
// replace to next spell
if(!talentCost && !IsPassiveSpell(prev_id))
m_charmInfo->AddSpellToAB(spell_id, prev_id);
learnSpell(prev_id); learnSpell(prev_id);
}
else else
learn_prev = false; learn_prev = false;
} }
// if remove last rank or non-ranked then update action bar at server and client if need // if remove last rank or non-ranked then update action bar at server and client if need
if(!learn_prev && m_charmInfo->AddSpellToAB(spell_id, 0)) if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id))
{ {
// need update action bar for last removed rank if(!m_loading)
if (Unit* owner = GetOwner()) {
if (owner->GetTypeId() == TYPEID_PLAYER) // need update action bar for last removed rank
((Player*)owner)->PetSpellInitialize(); if (Unit* owner = GetOwner())
if (owner->GetTypeId() == TYPEID_PLAYER)
((Player*)owner)->PetSpellInitialize();
}
} }
return true; return true;
} }
void Pet::CleanupActionBar()
{
for(int i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
if(UnitActionBarEntry const* ab = m_charmInfo->GetActionBarEntry(i))
if(ab->SpellOrAction && ab->IsActionBarForSpell() && !HasSpell(ab->SpellOrAction))
m_charmInfo->SetActionBar(i,0,ACT_DISABLED);
}
void Pet::InitPetCreateSpells() void Pet::InitPetCreateSpells()
{ {
m_charmInfo->InitPetActionBar(); m_charmInfo->InitPetActionBar();

View file

@ -65,6 +65,7 @@ enum PetSpellType
{ {
PETSPELL_NORMAL = 0, PETSPELL_NORMAL = 0,
PETSPELL_FAMILY = 1, PETSPELL_FAMILY = 1,
PETSPELL_TALENT = 2,
}; };
struct PetSpell struct PetSpell
@ -198,8 +199,9 @@ class Pet : public Creature
bool learnSpell(uint32 spell_id); bool learnSpell(uint32 spell_id);
void learnSpellHighRank(uint32 spellid); void learnSpellHighRank(uint32 spellid);
void InitLevelupSpellsForLevel(); void InitLevelupSpellsForLevel();
bool unlearnSpell(uint32 spell_id, bool learn_prev); bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
bool removeSpell(uint32 spell_id, bool learn_prev); bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
void CleanupActionBar();
PetSpellMap m_spells; PetSpellMap m_spells;
AutoSpellList m_autospells; AutoSpellList m_autospells;
@ -248,4 +250,4 @@ class Pet : public Creature
assert(false); assert(false);
} }
}; };
#endif #endif

View file

@ -357,7 +357,11 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, act_state); sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, act_state);
//if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add //ignore invalid position
if(position >= MAX_UNIT_ACTION_BAR_INDEX)
return;
//if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id))) if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id)))
{ {
//sign for autocast //sign for autocast
@ -377,8 +381,7 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
((Pet*)pet)->ToggleAutocast(spell_id, false); ((Pet*)pet)->ToggleAutocast(spell_id, false);
} }
charmInfo->GetActionBarEntry(position)->Type = act_state; charmInfo->SetActionBar(position,spell_id,ActiveStates(act_state));
charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id;
} }
} }
} }
@ -561,11 +564,7 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket )
else else
((Pet*)pet)->ToggleAutocast(spellid, state); ((Pet*)pet)->ToggleAutocast(spellid, state);
for(uint8 i = 0; i < 10; ++i) charmInfo->SetSpellAutocast(spellid,state);
{
if((charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) && spellid == charmInfo->GetActionBarEntry(i)->SpellOrAction)
charmInfo->GetActionBarEntry(i)->Type = state ? ACT_ENABLED : ACT_DISABLED;
}
} }
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )

View file

@ -2600,13 +2600,13 @@ void Player::RemoveMail(uint32 id)
} }
} }
void Player::SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError, uint32 item_guid, uint32 item_count) void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
{ {
WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_BAG_FULL?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0)))); WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_EQUIP_ERROR?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
data << (uint32) mailId; data << (uint32) mailId;
data << (uint32) mailAction; data << (uint32) mailAction;
data << (uint32) mailError; data << (uint32) mailError;
if ( mailError == MAIL_ERR_BAG_FULL ) if ( mailError == MAIL_ERR_EQUIP_ERROR )
data << (uint32) equipError; data << (uint32) equipError;
else if( mailAction == MAIL_ITEM_TAKEN ) else if( mailAction == MAIL_ITEM_TAKEN )
{ {
@ -8877,7 +8877,7 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
} }
if(pItem && pItem->IsBindedNotWith(GetGUID())) if(pItem && pItem->IsBindedNotWith(this))
{ {
if(no_space_count) if(no_space_count)
*no_space_count = count; *no_space_count = count;
@ -9360,7 +9360,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
return EQUIP_ERR_ITEM_NOT_FOUND; return EQUIP_ERR_ITEM_NOT_FOUND;
// item it 'bind' // item it 'bind'
if(pItem->IsBindedNotWith(GetGUID())) if(pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
Bag *pBag; Bag *pBag;
@ -9562,7 +9562,7 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
ItemPrototype const *pProto = pItem->GetProto(); ItemPrototype const *pProto = pItem->GetProto();
if( pProto ) if( pProto )
{ {
if(pItem->IsBindedNotWith(GetGUID())) if(pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
// check count of items (skip for auto move for same player from bank) // check count of items (skip for auto move for same player from bank)
@ -9735,7 +9735,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
if (!pProto) if (!pProto)
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
if (pItem->IsBindedNotWith(GetGUID())) if (pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
// check count of items (skip for auto move for same player from bank) // check count of items (skip for auto move for same player from bank)
@ -9915,7 +9915,7 @@ uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const
ItemPrototype const *pProto = pItem->GetProto(); ItemPrototype const *pProto = pItem->GetProto();
if (pProto) if (pProto)
{ {
if (pItem->IsBindedNotWith(GetGUID())) if (pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
if ((pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0) if ((pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0)
@ -16357,10 +16357,7 @@ void Player::PetSpellInitialize()
data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
// action bar loop // action bar loop
for(uint32 i = 0; i < 10; ++i) charmInfo->BuildActionBar(&data);
{
data << uint32(charmInfo->GetActionBarEntry(i)->Raw);
}
size_t spellsCountPos = data.wpos(); size_t spellsCountPos = data.wpos();
@ -16433,10 +16430,7 @@ void Player::PossessSpellInitialize()
data << uint32(0); data << uint32(0);
data << uint32(0); data << uint32(0);
for(uint32 i = 0; i < 10; ++i) charmInfo->BuildActionBar(&data);
{
data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
}
data << uint8(0); // spells count data << uint8(0); // spells count
data << uint8(0); // cooldowns count data << uint8(0); // cooldowns count
@ -16484,10 +16478,7 @@ void Player::CharmSpellInitialize()
else else
data << uint8(0) << uint8(0) << uint16(0); data << uint8(0) << uint8(0) << uint16(0);
for(uint32 i = 0; i < 10; ++i) charmInfo->BuildActionBar(&data);
{
data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
}
data << uint8(addlist); data << uint8(addlist);

View file

@ -1325,7 +1325,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void ClearComboPoints(); void ClearComboPoints();
void SendComboPoints(); void SendComboPoints();
void SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError = 0, uint32 item_guid = 0, uint32 item_count = 0); void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError = 0, uint32 item_guid = 0, uint32 item_count = 0);
void SendNewMail(); void SendNewMail();
void UpdateNextMailTimeAndUnreads(); void UpdateNextMailTimeAndUnreads();
void AddNewMailDeliverTime(time_t deliver_time); void AddNewMailDeliverTime(time_t deliver_time);

View file

@ -2515,4 +2515,32 @@ enum BattleGroundTypeId
}; };
#define MAX_BATTLEGROUND_TYPE_ID 12 #define MAX_BATTLEGROUND_TYPE_ID 12
enum MailResponseType
{
MAIL_SEND = 0,
MAIL_MONEY_TAKEN = 1,
MAIL_ITEM_TAKEN = 2,
MAIL_RETURNED_TO_SENDER = 3,
MAIL_DELETED = 4,
MAIL_MADE_PERMANENT = 5
};
enum MailResponseResult
{
MAIL_OK = 0,
MAIL_ERR_EQUIP_ERROR = 1,
MAIL_ERR_CANNOT_SEND_TO_SELF = 2,
MAIL_ERR_NOT_ENOUGH_MONEY = 3,
MAIL_ERR_RECIPIENT_NOT_FOUND = 4,
MAIL_ERR_NOT_YOUR_TEAM = 5,
MAIL_ERR_INTERNAL_ERROR = 6,
MAIL_ERR_DISABLED_FOR_TRIAL_ACC = 14,
MAIL_ERR_RECIPIENT_CAP_REACHED = 15,
MAIL_ERR_CANT_SEND_WRAPPED_COD = 16,
MAIL_ERR_MAIL_AND_CHAT_SUSPENDED = 17,
MAIL_ERR_TOO_MANY_ATTACHMENTS = 18,
MAIL_ERR_MAIL_ATTACHMENT_INVALID = 19,
MAIL_ERR_ITEM_HAS_EXPIRED = 21,
};
#endif #endif

View file

@ -10281,28 +10281,18 @@ void CharmInfo::InitPetActionBar()
// the first 3 SpellOrActions are attack, follow and stay // the first 3 SpellOrActions are attack, follow and stay
for(uint32 i = 0; i < 3; ++i) for(uint32 i = 0; i < 3; ++i)
{ {
PetActionBar[i].Type = ACT_COMMAND; SetActionBar(i,COMMAND_ATTACK - i,ACT_COMMAND);
PetActionBar[i].SpellOrAction = COMMAND_ATTACK - i; SetActionBar(i + 7,COMMAND_ATTACK - i,ACT_REACTION);
PetActionBar[i + 7].Type = ACT_REACTION;
PetActionBar[i + 7].SpellOrAction = COMMAND_ATTACK - i;
}
for(uint32 i=0; i < 4; ++i)
{
PetActionBar[i + 3].Type = ACT_DISABLED;
PetActionBar[i + 3].SpellOrAction = 0;
} }
for(uint32 i = 0; i < 4; ++i)
SetActionBar(i,0,ACT_DISABLED);
} }
void CharmInfo::InitEmptyActionBar() void CharmInfo::InitEmptyActionBar()
{ {
for(uint32 x = 1; x < 10; ++x) SetActionBar(0,COMMAND_ATTACK,ACT_COMMAND);
{ for(uint32 x = 1; x < MAX_UNIT_ACTION_BAR_INDEX; ++x)
PetActionBar[x].Type = ACT_PASSIVE; SetActionBar(x,0,ACT_PASSIVE);
PetActionBar[x].SpellOrAction = 0;
}
PetActionBar[0].Type = ACT_COMMAND;
PetActionBar[0].SpellOrAction = COMMAND_ATTACK;
} }
void CharmInfo::InitPossessCreateSpells() void CharmInfo::InitPossessCreateSpells()
@ -10317,7 +10307,7 @@ void CharmInfo::InitPossessCreateSpells()
if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x])) if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x]))
m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true); m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true);
else else
AddSpellToAB(0, ((Creature*)m_unit)->m_spells[x], ACT_PASSIVE); AddSpellToActionBar(((Creature*)m_unit)->m_spells[x], ACT_PASSIVE);
} }
} }
@ -10362,39 +10352,56 @@ void CharmInfo::InitCharmCreateSpells()
else else
newstate = ACT_PASSIVE; newstate = ACT_PASSIVE;
AddSpellToAB(0, spellId, newstate); AddSpellToActionBar(spellId, newstate);
} }
} }
} }
bool CharmInfo::AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate) bool CharmInfo::AddSpellToActionBar(uint32 spell_id, ActiveStates newstate)
{ {
// new spell already listed for example in case prepered switch to lesser rank in Pet::removeSpell uint32 first_id = spellmgr.GetFirstSpellInChain(spell_id);
for(uint8 i = 0; i < 10; ++i)
if (PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_PASSIVE)
if (newid && PetActionBar[i].SpellOrAction == newid)
return true;
// old spell can be leasted for example in case learn high rank // new spell rank can be already listed
for(uint8 i = 0; i < 10; ++i) for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{ {
if (PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_PASSIVE) if (PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell())
{ {
if (PetActionBar[i].SpellOrAction == oldid) if (spellmgr.GetFirstSpellInChain(PetActionBar[i].SpellOrAction) == first_id)
{ {
PetActionBar[i].SpellOrAction = newid; PetActionBar[i].SpellOrAction = spell_id;
if (!oldid)
{
if (newstate == ACT_DECIDE)
PetActionBar[i].Type = ACT_DISABLED;
else
PetActionBar[i].Type = newstate;
}
return true; return true;
} }
} }
} }
// or use empty slot in other case
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if (!PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell())
{
SetActionBar(i,spell_id,newstate == ACT_DECIDE ? ACT_DISABLED : newstate);
return true;
}
}
return false;
}
bool CharmInfo::RemoveSpellFromActionBar(uint32 spell_id)
{
uint32 first_id = spellmgr.GetFirstSpellInChain(spell_id);
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if (PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell())
{
if (spellmgr.GetFirstSpellInChain(PetActionBar[i].SpellOrAction) == first_id)
{
SetActionBar(i,0,ACT_DISABLED);
return true;
}
}
}
return false; return false;
} }
@ -10421,6 +10428,50 @@ void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0); m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
} }
bool CharmInfo::LoadActionBar( std::string data )
{
Tokens tokens = StrSplit(data, " ");
if (tokens.size() != MAX_UNIT_ACTION_BAR_INDEX*2)
return false;
int index;
Tokens::iterator iter;
for(iter = tokens.begin(), index = 0; index < MAX_UNIT_ACTION_BAR_INDEX; ++iter, ++index )
{
// use unsigned cast to avoid sign negative format use at long-> ActiveStates (int) conversion
PetActionBar[index].Type = atol((*iter).c_str());
++iter;
PetActionBar[index].SpellOrAction = atol((*iter).c_str());
// check correctness
if(PetActionBar[index].IsActionBarForSpell() && !sSpellStore.LookupEntry(PetActionBar[index].SpellOrAction))
SetActionBar(index,0,ACT_DISABLED);
}
return true;
}
void CharmInfo::BuildActionBar( WorldPacket* data )
{
for(uint32 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
*data << uint16(PetActionBar[i].SpellOrAction);
*data << uint16(PetActionBar[i].Type);
}
}
void CharmInfo::SetSpellAutocast( uint32 spell_id, bool state )
{
for(int i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if(spell_id == PetActionBar[i].SpellOrAction && PetActionBar[i].IsActionBarForSpell())
{
PetActionBar[i].Type = state ? ACT_ENABLED : ACT_DISABLED;
break;
}
}
}
bool Unit::isFrozen() const bool Unit::isFrozen() const
{ {
return HasAuraState(AURA_STATE_FROZEN); return HasAuraState(AURA_STATE_FROZEN);

View file

@ -714,24 +714,6 @@ struct SpellNonMeleeDamage{
uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition); uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition);
struct UnitActionBarEntry
{
UnitActionBarEntry() : Raw(0) {}
union
{
struct
{
uint16 SpellOrAction;
uint16 Type;
};
struct
{
uint32 Raw;
};
};
};
#define MAX_DECLINED_NAME_CASES 5 #define MAX_DECLINED_NAME_CASES 5
struct DeclinedName struct DeclinedName
@ -774,12 +756,28 @@ enum CommandStates
COMMAND_ABANDON = 3 COMMAND_ABANDON = 3
}; };
struct UnitActionBarEntry
{
UnitActionBarEntry() : SpellOrAction(0), Type(ACT_DISABLED) {}
uint16 SpellOrAction;
uint16 Type;
// helper
bool IsActionBarForSpell() const
{
return Type == ACT_DISABLED || Type == ACT_ENABLED || Type == ACT_PASSIVE;
}
};
struct CharmSpellEntry struct CharmSpellEntry
{ {
uint16 spellId; uint16 spellId;
uint16 active; uint16 active;
}; };
#define MAX_UNIT_ACTION_BAR_INDEX 10
struct CharmInfo struct CharmInfo
{ {
public: public:
@ -798,15 +796,27 @@ struct CharmInfo
void InitCharmCreateSpells(); void InitCharmCreateSpells();
void InitPetActionBar(); void InitPetActionBar();
void InitEmptyActionBar(); void InitEmptyActionBar();
//return true if successful //return true if successful
bool AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate = ACT_DECIDE); bool AddSpellToActionBar(uint32 spellid, ActiveStates newstate = ACT_DECIDE);
bool RemoveSpellFromActionBar(uint32 spell_id);
bool LoadActionBar(std::string data);
void BuildActionBar(WorldPacket* data);
void SetSpellAutocast(uint32 spell_id, bool state);
void SetActionBar(uint8 index, uint32 spellOrAction,ActiveStates type)
{
PetActionBar[index].Type = type;
PetActionBar[index].SpellOrAction = spellOrAction;
}
UnitActionBarEntry const* GetActionBarEntry(uint8 index) const { return &(PetActionBar[index]); }
void ToggleCreatureAutocast(uint32 spellid, bool apply); void ToggleCreatureAutocast(uint32 spellid, bool apply);
UnitActionBarEntry* GetActionBarEntry(uint8 index) { return &(PetActionBar[index]); }
CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); } CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
private: private:
Unit* m_unit; Unit* m_unit;
UnitActionBarEntry PetActionBar[10]; UnitActionBarEntry PetActionBar[MAX_UNIT_ACTION_BAR_INDEX];
CharmSpellEntry m_charmspells[4]; CharmSpellEntry m_charmspells[4];
CommandStates m_CommandState; CommandStates m_CommandState;
ReactStates m_reactState; ReactStates m_reactState;

View file

@ -19,11 +19,11 @@
#ifndef MANGOSSERVER_ERRORS_H #ifndef MANGOSSERVER_ERRORS_H
#define MANGOSSERVER_ERRORS_H #define MANGOSSERVER_ERRORS_H
#define WPAssert( assertion ) { if( !(assertion) ) { fprintf( stderr, "\n%s:%i ASSERTION FAILED:\n %s\n", __FILE__, __LINE__, #assertion ); assert( #assertion &&0 ); } } #define WPAssert( assertion ) { if( !(assertion) ) { fprintf( stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n", __FILE__, __LINE__,__FUNCTION__, #assertion ); assert( #assertion &&0 ); } }
#define WPError( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i ERROR:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); assert( false ); } #define WPError( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( false ); }
#define WPWarning( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i WARNING:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); } #define WPWarning( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "\n%s:%i in %s WARNING:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); }
#define WPFatal( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i FATAL ERROR:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); assert( #assertion &&0 ); abort(); } #define WPFatal( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "\n%s:%i in %s FATAL ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( #assertion &&0 ); abort(); }
#define ASSERT WPAssert #define ASSERT WPAssert
#endif #endif

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 "7895" #define REVISION_NR "7903"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__