diff --git a/sql/characters.sql b/sql/characters.sql index c20530a05..fc717c1ef 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `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'; -- @@ -472,7 +472,6 @@ CREATE TABLE `character_pet` ( `level` int(11) unsigned NOT NULL default '1', `exp` int(11) 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', `renamed` tinyint(1) unsigned NOT NULL default '0', `slot` int(11) unsigned NOT NULL default '0', diff --git a/sql/mangos.sql b/sql/mangos.sql index c9553fb49..09a093fa4 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `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'; -- @@ -820,8 +820,8 @@ CREATE TABLE `creature_template` ( `family` tinyint(4) NOT NULL default '0', `trainer_type` tinyint(4) NOT NULL default '0', `trainer_spell` mediumint(8) unsigned NOT NULL default '0', - `class` tinyint(3) unsigned NOT NULL default '0', - `race` tinyint(3) unsigned NOT NULL default '0', + `trainer_class` tinyint(3) unsigned NOT NULL default '0', + `trainer_race` tinyint(3) unsigned NOT NULL default '0', `minrangedmg` float NOT NULL default '0', `maxrangedmg` float 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', `chance` float unsigned NOT NULL default '0', `description` varchar(255) NOT NULL, - PRIMARY KEY (`pool_entry`,`guid`) + PRIMARY KEY (`pool_entry`,`guid`), + INDEX `idx_guid`(`guid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; @@ -12961,7 +12962,8 @@ CREATE TABLE `pool_gameobject` ( `pool_entry` mediumint(8) unsigned NOT NULL default '0', `chance` float unsigned NOT NULL default '0', `description` varchar(255) NOT NULL, - PRIMARY KEY (`guid`,`pool_entry`) + PRIMARY KEY (`guid`,`pool_entry`), + INDEX `idx_guid`(`guid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- diff --git a/sql/updates/7896_01_mangos_creature_template.sql b/sql/updates/7896_01_mangos_creature_template.sql new file mode 100644 index 000000000..511460958 --- /dev/null +++ b/sql/updates/7896_01_mangos_creature_template.sql @@ -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'; diff --git a/sql/updates/7902_01_mangos_pool_creature.sql b/sql/updates/7902_01_mangos_pool_creature.sql new file mode 100644 index 000000000..309ca5e38 --- /dev/null +++ b/sql/updates/7902_01_mangos_pool_creature.sql @@ -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`); \ No newline at end of file diff --git a/sql/updates/7902_02_mangos_pool_gameobject.sql b/sql/updates/7902_02_mangos_pool_gameobject.sql new file mode 100644 index 000000000..46524337a --- /dev/null +++ b/sql/updates/7902_02_mangos_pool_gameobject.sql @@ -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`); \ No newline at end of file diff --git a/sql/updates/7903_01_characters_character_pet.sql b/sql/updates/7903_01_characters_character_pet.sql new file mode 100644 index 000000000..834aac200 --- /dev/null +++ b/sql/updates/7903_01_characters_character_pet.sql @@ -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`; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 3470dcb72..6e18e6493 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -200,6 +200,10 @@ pkgdata_DATA = \ 7886_01_mangos_petcreateinfo_spell.sql \ 7887_01_characters_character_pet.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 ## Additional files to include when running 'make dist' @@ -380,4 +384,8 @@ EXTRA_DIST = \ 7886_01_mangos_petcreateinfo_spell.sql \ 7887_01_characters_character_pet.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 diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 819c16104..f8eade957 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -605,12 +605,12 @@ bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const switch(GetCreatureInfo()->trainer_type) { case TRAINER_TYPE_CLASS: - if(pPlayer->getClass()!=GetCreatureInfo()->classNum) + if(pPlayer->getClass()!=GetCreatureInfo()->trainer_class) { if(msg) { pPlayer->PlayerTalkClass->ClearMenus(); - switch(GetCreatureInfo()->classNum) + switch(GetCreatureInfo()->trainer_class) { case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break; case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break; @@ -635,12 +635,12 @@ bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const } break; case TRAINER_TYPE_MOUNTS: - if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race) + if(GetCreatureInfo()->trainer_race && pPlayer->getRace() != GetCreatureInfo()->trainer_race) { if(msg) { pPlayer->PlayerTalkClass->ClearMenus(); - switch(GetCreatureInfo()->classNum) + switch(GetCreatureInfo()->trainer_class) { case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,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 && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS - && pPlayer->getClass() == GetCreatureInfo()->classNum; + && pPlayer->getClass() == GetCreatureInfo()->trainer_class; } void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid ) @@ -771,7 +771,7 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid ) cantalking=false; break; 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; break; case GOSSIP_OPTION_TAXIVENDOR: diff --git a/src/game/Creature.h b/src/game/Creature.h index 2bb6fe004..c33da87ba 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -190,8 +190,8 @@ struct CreatureInfo uint32 family; // enum CreatureFamily values (optional) uint32 trainer_type; uint32 trainer_spell; - uint32 classNum; - uint32 race; + uint32 trainer_class; + uint32 trainer_race; float minrangedmg; float maxrangedmg; uint32 rangedattackpower; diff --git a/src/game/Item.cpp b/src/game/Item.cpp index ea7707394..82db45c4d 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -696,18 +696,19 @@ bool Item::IsEquipped() const return !IsInBag() && m_slot < EQUIPMENT_SLOT_END; } -bool Item::CanBeTraded() const +bool Item::CanBeTraded(bool mail) const { - if(IsSoulBound()) - return false; - if(IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()) ) + if ((!mail || !IsBoundAccountWide()) && IsSoulBound()) 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; - if(owner->GetLootGUID()==GetGUID()) + if (owner->GetLootGUID()==GetGUID()) return false; } @@ -941,3 +942,29 @@ Item* Item::CloneItem( uint32 count, Player const* player ) const newItem->SetItemRandomProperties(GetItemRandomPropertyId()); 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(); + } +} \ No newline at end of file diff --git a/src/game/Item.h b/src/game/Item.h index c49d3a946..8ec9f47a2 100644 --- a/src/game/Item.h +++ b/src/game/Item.h @@ -116,8 +116,11 @@ enum InventoryChangeFailure EQUIP_ERR_TOO_MUCH_GOLD = 77, EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78, EQUIP_ERR_CANNOT_TRADE_THAT = 79, - EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80 - // probably exist more + EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80, + // no output = 81, + EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS = 82, + // no output = 83, + // crash client = 84, }; 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); } bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); } - bool IsAccountBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); } - bool IsBindedNotWith(uint64 guid) const { return IsSoulBound() && GetOwnerGUID()!= guid; } + bool IsBoundAccountWide() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); } + bool IsBindedNotWith(Player const* player) const; bool IsBoundByEnchant() const; virtual void SaveToDB(); 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 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; } bool IsInTrade() const { return mb_in_trade; } diff --git a/src/game/ItemPrototype.h b/src/game/ItemPrototype.h index 48b6f3f51..9974030ac 100644 --- a/src/game/ItemPrototype.h +++ b/src/game/ItemPrototype.h @@ -101,7 +101,7 @@ enum ItemBondingType // masks for ITEM_FIELD_FLAGS field 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_OPENABLE = 0x00000004, ITEM_FLAGS_WRAPPED = 0x00000008, @@ -114,7 +114,7 @@ enum ITEM_FLAGS 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_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 }; diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index b1eb9ca44..72fe5946a 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -77,7 +77,10 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) recv_data >> items_count; // attached items count if(items_count > 12) // client limit + { + GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); return; + } // 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); @@ -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", 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; } @@ -121,7 +124,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) 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; } @@ -131,7 +134,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) if (pl->GetMoney() < reqmoney) { - pl->SendMailResult(0, 0, MAIL_ERR_NOT_ENOUGH_MONEY); + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); 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.. if (mails_count > 100) { - pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // test the receiver's Faction... 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; } + uint32 rc_account = 0; + if(receive) + rc_account = receive->GetSession()->GetAccountId(); + else + rc_account = objmgr.GetPlayerAccountIdByGUID(rc); + if (items_count) { for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) @@ -177,31 +186,44 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) if(!mailItem.item_guidlow) { - pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM)); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) - if(!mailItem.item || !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; } + + if(!mailItem.item->CanBeTraded(true)) + { + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); + return; + } + + if(mailItem.item->IsBoundAccountWide() && mailItem.item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) + { + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); + return; + } + if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION)) { - pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { - pl->SendMailResult(0, 0, MAIL_ERR_CANT_SEND_WRAPPED_COD); + pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } } } - pl->SendMailResult(0, 0, MAIL_OK); + pl->SendMailResult(0, MAIL_SEND, MAIL_OK); uint32 itemTextId = 0; if (!body.empty()) @@ -216,12 +238,6 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) 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) { 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); if(m) m->state = MAIL_STATE_DELETED; - pl->SendMailResult(mailId, MAIL_DELETED, 0); + pl->SendMailResult(mailId, MAIL_DELETED, MAIL_OK); } 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); 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 ) @@ -508,7 +524,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); } 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 ) @@ -532,7 +548,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data ) return; } - pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, 0); + pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK); pl->ModifyMoney(m->money); m->money = 0; @@ -725,11 +741,11 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) pl->StoreItem(dest, bodyItem, true); //bodyItem->SetState(ITEM_NEW, pl); is set automatically - pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, 0); + pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK); } 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; } } diff --git a/src/game/Mail.h b/src/game/Mail.h index ba223a890..7f7398da8 100644 --- a/src/game/Mail.h +++ b/src/game/Mail.h @@ -26,30 +26,6 @@ class Item; #define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889 #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 { MAIL_CHECK_MASK_NONE = 0, diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 371659b0c..ed6656832 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -497,15 +497,15 @@ void ObjectMgr::LoadCreatureTemplates() 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; } - 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; } diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index b28853183..019f020a5 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -87,25 +87,25 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool QueryResult *result; 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 - 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 " + // 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, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " "FROM character_pet WHERE owner = '%u' AND id = '%u'", ownerid, petnumber); 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 - 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 " + // 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, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " "FROM character_pet WHERE owner = '%u' AND slot = '%u'", ownerid, PET_SAVE_AS_CURRENT ); else if (petentry) // 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 - 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 " + // 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, 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') ", ownerid, petentry,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); else // 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 - 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 " + // 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, 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') ", 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; } - uint32 summon_spell_id = fields[18].GetUInt32(); + uint32 summon_spell_id = fields[17].GetUInt32(); SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id); bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0; @@ -164,7 +164,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool return false; } - setPetType(PetType(fields[19].GetUInt8())); + setPetType(PetType(fields[18].GetUInt8())); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, owner->getFaction()); 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()); uint32 petlevel = fields[4].GetUInt32(); SetUInt32Value(UNIT_NPC_FLAGS, 0); - SetName(fields[9].GetString()); + SetName(fields[8].GetString()); switch (getPetType()) { @@ -197,14 +197,13 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool break; case HUNTER_PET: 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, 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); // this enables popup window (pet abandon, cancel) SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS)); - SetPower(POWER_HAPPINESS, fields[13].GetUInt32()); + SetPower(POWER_HAPPINESS, fields[12].GetUInt32()); setPowerType(POWER_FOCUS); break; default: @@ -215,20 +214,22 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool SetPvP(true); InitStatsForLevel(petlevel); + InitTalentForLevel(); // set original talents points before spell loading + SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32()); SetCreatorGUID(owner->GetGUID()); m_charmInfo->SetReactState(ReactStates(fields[6].GetUInt8())); - uint32 savedhealth = fields[11].GetUInt32(); - uint32 savedmana = fields[12].GetUInt32(); + uint32 savedhealth = fields[10].GetUInt32(); + uint32 savedmana = fields[11].GetUInt32(); // set current pet as current // 0=current // 1..MAX_PET_STABLES in stable slot // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) - if (fields[8].GetUInt32() != 0) + if (fields[7].GetUInt32() != 0) { CharacterDatabase.BeginTransaction(); 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) { - // permanent controlled pets store state in DB - Tokens tokens = StrSplit(fields[14].GetString(), " "); - - if (tokens.size() != 20) + if(!m_charmInfo->LoadActionBar(fields[13].GetCppString())) { delete result; 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) - uint32 timediff = (time(NULL) - fields[15].GetUInt32()); + uint32 timediff = (time(NULL) - fields[14].GetUInt32()); - m_resetTalentsCost = fields[16].GetUInt32(); - m_resetTalentsTime = fields[17].GetUInt64(); + m_resetTalentsCost = fields[15].GetUInt32(); + m_resetTalentsTime = fields[16].GetUInt64(); 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 _LoadSpells(); + InitLevelupSpellsForLevel(); + + CleanupActionBar(); // remove unknown spells from action bar after load + _LoadSpellCooldowns(); 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; SynchronizeLevelWithOwner(); @@ -404,7 +395,7 @@ void Pet::SavePetToDB(PetSaveMode mode) owner,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); // save pet 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 (" << m_charmInfo->GetPetNumber() << ", " << GetEntry() << ", " @@ -413,7 +404,6 @@ void Pet::SavePetToDB(PetSaveMode mode) << getLevel() << ", " << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " << uint32(m_charmInfo->GetReactState()) << ", " - << uint32(GetFreeTalentPoints()) << ", " << uint32(mode) << ", '" << name.c_str() << "', " << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " @@ -421,8 +411,12 @@ void Pet::SavePetToDB(PetSaveMode mode) << curmana << ", " << GetPower(POWER_HAPPINESS) << ", '"; - for(uint32 i = 0; i < 10; ++i) - ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; + 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 << "', " << time(NULL) << ", " << uint32(m_resetTalentsCost) << ", " @@ -1099,7 +1093,17 @@ void Pet::_LoadSpells() { 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() ); @@ -1113,8 +1117,8 @@ void Pet::_SaveSpells() { ++next; - // prevent saving family passives to DB - if (itr->second.type == PETSPELL_FAMILY) + // save only talent spells for pets, other spells auto-applied + if (itr->second.type != PETSPELL_TALENT) continue; 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) 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 )) { 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 if(!HasSpell(rankSpellId)) 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); oldspell_id = itr2->first; - unlearnSpell(itr2->first,false); + unlearnSpell(itr2->first,false,false); break; } // ignore new lesser rank @@ -1373,7 +1380,7 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel if (IsPassiveSpell(spell_id)) CastSpell(this, spell_id, true); else - m_charmInfo->AddSpellToAB(oldspell_id, spell_id); + m_charmInfo->AddSpellToActionBar(spell_id); if(newspell.active == ACT_ENABLED) ToggleAutocast(spell_id, true); @@ -1396,16 +1403,17 @@ bool Pet::learnSpell(uint32 spell_id) if (!addSpell(spell_id)) return false; - Unit* owner = GetOwner(); - if(owner && owner->GetTypeId() == TYPEID_PLAYER) + if(!m_loading) { - if(!m_loading) + Unit* owner = GetOwner(); + if(owner && owner->GetTypeId() == TYPEID_PLAYER) { WorldPacket data(SMSG_PET_LEARNED_SPELL, 2); data << uint16(spell_id); ((Player*)owner)->GetSession()->SendPacket(&data); + + ((Player*)owner)->PetSpellInitialize(); } - ((Player*)owner)->PetSpellInitialize(); } return true; } @@ -1441,7 +1449,7 @@ void Pet::InitLevelupSpellsForLevel() // will called first if level down if(spellEntry->spellLevel > level) - unlearnSpell(spellEntry->Id,false); + unlearnSpell(spellEntry->Id,true); // will called if level up else 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) { @@ -1467,7 +1475,7 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev) 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); if (itr == m_spells.end()) @@ -1498,29 +1506,35 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev) if (learn_prev) { 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); - } else learn_prev = false; } // 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 (Unit* owner = GetOwner()) - if (owner->GetTypeId() == TYPEID_PLAYER) - ((Player*)owner)->PetSpellInitialize(); + if(!m_loading) + { + // need update action bar for last removed rank + if (Unit* owner = GetOwner()) + if (owner->GetTypeId() == TYPEID_PLAYER) + ((Player*)owner)->PetSpellInitialize(); + } } 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() { m_charmInfo->InitPetActionBar(); diff --git a/src/game/Pet.h b/src/game/Pet.h index f8de3ed5e..223675a88 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -65,6 +65,7 @@ enum PetSpellType { PETSPELL_NORMAL = 0, PETSPELL_FAMILY = 1, + PETSPELL_TALENT = 2, }; struct PetSpell @@ -198,8 +199,9 @@ class Pet : public Creature bool learnSpell(uint32 spell_id); void learnSpellHighRank(uint32 spellid); void InitLevelupSpellsForLevel(); - bool unlearnSpell(uint32 spell_id, bool learn_prev); - bool removeSpell(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 clear_ab = true); + void CleanupActionBar(); PetSpellMap m_spells; AutoSpellList m_autospells; @@ -248,4 +250,4 @@ class Pet : public Creature assert(false); } }; -#endif +#endif \ No newline at end of file diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 3c641cbf6..103f2a59d 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -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); - //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))) { //sign for autocast @@ -377,8 +381,7 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) ((Pet*)pet)->ToggleAutocast(spell_id, false); } - charmInfo->GetActionBarEntry(position)->Type = act_state; - charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id; + charmInfo->SetActionBar(position,spell_id,ActiveStates(act_state)); } } } @@ -561,11 +564,7 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) else ((Pet*)pet)->ToggleAutocast(spellid, state); - for(uint8 i = 0; i < 10; ++i) - { - 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; - } + charmInfo->SetSpellAutocast(spellid,state); } void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index ecfdb2779..3a315cbb3 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -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) mailAction; data << (uint32) mailError; - if ( mailError == MAIL_ERR_BAG_FULL ) + if ( mailError == MAIL_ERR_EQUIP_ERROR ) data << (uint32) equipError; 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; } - if(pItem && pItem->IsBindedNotWith(GetGUID())) + if(pItem && pItem->IsBindedNotWith(this)) { if(no_space_count) *no_space_count = count; @@ -9360,7 +9360,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const return EQUIP_ERR_ITEM_NOT_FOUND; // item it 'bind' - if(pItem->IsBindedNotWith(GetGUID())) + if(pItem->IsBindedNotWith(this)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; Bag *pBag; @@ -9562,7 +9562,7 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo ItemPrototype const *pProto = pItem->GetProto(); if( pProto ) { - if(pItem->IsBindedNotWith(GetGUID())) + if(pItem->IsBindedNotWith(this)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; // 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) 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; // 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(); if (pProto) { - if (pItem->IsBindedNotWith(GetGUID())) + if (pItem->IsBindedNotWith(this)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; 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); // action bar loop - for(uint32 i = 0; i < 10; ++i) - { - data << uint32(charmInfo->GetActionBarEntry(i)->Raw); - } + charmInfo->BuildActionBar(&data); size_t spellsCountPos = data.wpos(); @@ -16433,10 +16430,7 @@ void Player::PossessSpellInitialize() data << uint32(0); data << uint32(0); - for(uint32 i = 0; i < 10; ++i) - { - data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type); - } + charmInfo->BuildActionBar(&data); data << uint8(0); // spells count data << uint8(0); // cooldowns count @@ -16484,10 +16478,7 @@ void Player::CharmSpellInitialize() else data << uint8(0) << uint8(0) << uint16(0); - for(uint32 i = 0; i < 10; ++i) - { - data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type); - } + charmInfo->BuildActionBar(&data); data << uint8(addlist); diff --git a/src/game/Player.h b/src/game/Player.h index eb5536831..54e5f8cdb 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1325,7 +1325,7 @@ class MANGOS_DLL_SPEC Player : public Unit void ClearComboPoints(); 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 UpdateNextMailTimeAndUnreads(); void AddNewMailDeliverTime(time_t deliver_time); diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 3c2e12d32..8234a9317 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -2515,4 +2515,32 @@ enum BattleGroundTypeId }; #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 diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 9111222f8..41cf0cf66 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -10281,28 +10281,18 @@ void CharmInfo::InitPetActionBar() // the first 3 SpellOrActions are attack, follow and stay for(uint32 i = 0; i < 3; ++i) { - PetActionBar[i].Type = ACT_COMMAND; - PetActionBar[i].SpellOrAction = COMMAND_ATTACK - i; - - 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; + SetActionBar(i,COMMAND_ATTACK - i,ACT_COMMAND); + SetActionBar(i + 7,COMMAND_ATTACK - i,ACT_REACTION); } + for(uint32 i = 0; i < 4; ++i) + SetActionBar(i,0,ACT_DISABLED); } void CharmInfo::InitEmptyActionBar() { - for(uint32 x = 1; x < 10; ++x) - { - PetActionBar[x].Type = ACT_PASSIVE; - PetActionBar[x].SpellOrAction = 0; - } - PetActionBar[0].Type = ACT_COMMAND; - PetActionBar[0].SpellOrAction = COMMAND_ATTACK; + SetActionBar(0,COMMAND_ATTACK,ACT_COMMAND); + for(uint32 x = 1; x < MAX_UNIT_ACTION_BAR_INDEX; ++x) + SetActionBar(x,0,ACT_PASSIVE); } void CharmInfo::InitPossessCreateSpells() @@ -10317,7 +10307,7 @@ void CharmInfo::InitPossessCreateSpells() if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x])) m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true); 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 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 - 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; + uint32 first_id = spellmgr.GetFirstSpellInChain(spell_id); - // old spell can be leasted for example in case learn high rank - for(uint8 i = 0; i < 10; ++i) + // new spell rank can be already listed + 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; - if (!oldid) - { - if (newstate == ACT_DECIDE) - PetActionBar[i].Type = ACT_DISABLED; - else - PetActionBar[i].Type = newstate; - } - + PetActionBar[i].SpellOrAction = spell_id; 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; } @@ -10421,6 +10428,50 @@ void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow) 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 { return HasAuraState(AURA_STATE_FROZEN); diff --git a/src/game/Unit.h b/src/game/Unit.h index 50ee9594f..ba1fc153f 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -714,24 +714,6 @@ struct SpellNonMeleeDamage{ 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 struct DeclinedName @@ -774,12 +756,28 @@ enum CommandStates 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 { uint16 spellId; uint16 active; }; +#define MAX_UNIT_ACTION_BAR_INDEX 10 + struct CharmInfo { public: @@ -798,15 +796,27 @@ struct CharmInfo void InitCharmCreateSpells(); void InitPetActionBar(); void InitEmptyActionBar(); + //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); - UnitActionBarEntry* GetActionBarEntry(uint8 index) { return &(PetActionBar[index]); } CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); } private: + Unit* m_unit; - UnitActionBarEntry PetActionBar[10]; + UnitActionBarEntry PetActionBar[MAX_UNIT_ACTION_BAR_INDEX]; CharmSpellEntry m_charmspells[4]; CommandStates m_CommandState; ReactStates m_reactState; diff --git a/src/shared/Errors.h b/src/shared/Errors.h index 546da27b1..06bcc8823 100644 --- a/src/shared/Errors.h +++ b/src/shared/Errors.h @@ -19,11 +19,11 @@ #ifndef 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 WPError( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i ERROR:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); assert( false ); } -#define WPWarning( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i WARNING:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); } +#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( "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( false ); } +#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 #endif diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 72f6803f3..de1b34343 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7895" + #define REVISION_NR "7903" #endif // __REVISION_NR_H__