diff options
24 files changed, 449 insertions, 16 deletions
diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index 0a7528d4ca8..54ebdf24e15 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -194,6 +194,30 @@ LOCK TABLES `battlenet_account_bans` WRITE; UNLOCK TABLES; -- +-- Table structure for table `battlenet_account_toys` +-- + +DROP TABLE IF EXISTS `battlenet_account_toys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `battlenet_account_toys` ( + `accountId` int(10) unsigned NOT NULL, + `itemId` int(11) NOT NULL DEFAULT '0', + `isFavourite` tinyint(1) DEFAULT '0', + PRIMARY KEY (`accountId`,`itemId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `battlenet_account_toys` +-- + +LOCK TABLES `battlenet_account_toys` WRITE; +/*!40000 ALTER TABLE `battlenet_account_toys` DISABLE KEYS */; +/*!40000 ALTER TABLE `battlenet_account_toys` ENABLE KEYS */; +UNLOCK TABLES; + +-- -- Table structure for table `battlenet_accounts` -- @@ -671,4 +695,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2015-07-13 23:49:27 +-- Dump completed on 2015-08-25 22:49:45 diff --git a/sql/updates/auth/2015_08_26_00_auth.sql b/sql/updates/auth/2015_08_26_00_auth.sql new file mode 100644 index 00000000000..e01f9c50a24 --- /dev/null +++ b/sql/updates/auth/2015_08_26_00_auth.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS `battlenet_account_toys`; + +CREATE TABLE `battlenet_account_toys` ( + `accountId` int(10) unsigned NOT NULL, + `itemId` int(11) NOT NULL DEFAULT '0', + `isFavourite` tinyint(1) DEFAULT '0', + PRIMARY KEY (`accountId`,`itemId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/sql/updates/hotfixes/2015_08_26_00_hotfixes.sql b/sql/updates/hotfixes/2015_08_26_00_hotfixes.sql new file mode 100644 index 00000000000..3ccd8ac3eeb --- /dev/null +++ b/sql/updates/hotfixes/2015_08_26_00_hotfixes.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS `toy`; + +CREATE TABLE `toy` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `ItemID` int(10) unsigned NOT NULL DEFAULT '0', + `Flags` int(10) unsigned NOT NULL DEFAULT '0', + `Description` text, + `CategoryFilter` int(10) NOT NULL DEFAULT '0', + `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `toy_locale`; + +CREATE TABLE `toy_locale` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `locale` varchar(4) NOT NULL, + `Description_lang` text NOT NULL, + `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`locale`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index c037f1bce70..ff3f8e906c7 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -417,6 +417,10 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_TOTEM_CATEGORY, "SELECT ID, Name, CategoryType, CategoryMask FROM totem_category ORDER BY ID DESC", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_TOTEM_CATEGORY, "SELECT ID, Name_lang FROM totem_category_locale WHERE locale = ?", CONNECTION_SYNCH); + // Toy.db2 + PrepareStatement(HOTFIX_SEL_TOY, "SELECT ID, ItemID, Flags, Description, CategoryFilter FROM toy ORDER BY ID DESC", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_TOY, "SELECT ID, Description_lang FROM toy_locale WHERE locale = ?", CONNECTION_SYNCH); + // TransportAnimation.db2 PrepareStatement(HOTFIX_SEL_TRANSPORT_ANIMATION, "SELECT ID, TransportID, TimeIndex, PosX, PosY, PosZ, SequenceID FROM transport_animation" " ORDER BY ID DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index c92b7b23499..7740c78afc3 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -247,6 +247,9 @@ enum HotfixDatabaseStatements HOTFIX_SEL_TOTEM_CATEGORY, HOTFIX_SEL_TOTEM_CATEGORY_LOCALE, + HOTFIX_SEL_TOY, + HOTFIX_SEL_TOY_LOCALE, + HOTFIX_SEL_TRANSPORT_ANIMATION, HOTFIX_SEL_TRANSPORT_ROTATION, diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index a3d04e03c79..869dc617246 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -140,4 +140,8 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_LAST_CHAR_UNDELETE, "SELECT LastCharacterUndelete FROM battlenet_accounts WHERE Id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LAST_CHAR_UNDELETE, "UPDATE battlenet_accounts SET LastCharacterUndelete = UNIX_TIMESTAMP() WHERE Id = ?", CONNECTION_ASYNC); + + // Account wide toys + PrepareStatement(LOGIN_SEL_ACCOUNT_TOYS, "SELECT itemId, isFavourite FROM battlenet_account_toys WHERE accountId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_REP_ACCOUNT_TOYS, "REPLACE INTO battlenet_account_toys (accountId, itemId, isFavourite) VALUES (?, ?, ?)", CONNECTION_ASYNC); } diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 06d13f29da3..efef4e6ee36 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -143,6 +143,9 @@ enum LoginDatabaseStatements LOGIN_SEL_LAST_CHAR_UNDELETE, LOGIN_UPD_LAST_CHAR_UNDELETE, + LOGIN_SEL_ACCOUNT_TOYS, + LOGIN_REP_ACCOUNT_TOYS, + MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 9a9a8250248..88ed3989efa 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -112,6 +112,7 @@ DB2Storage<TaxiNodesEntry> sTaxiNodesStore("TaxiNodes.db2", DB2Storage<TaxiPathEntry> sTaxiPathStore("TaxiPath.db2", TaxiPathFormat, HOTFIX_SEL_TAXI_PATH); DB2Storage<TaxiPathNodeEntry> sTaxiPathNodeStore("TaxiPathNode.db2", TaxiPathNodeFormat, HOTFIX_SEL_TAXI_PATH_NODE); DB2Storage<TotemCategoryEntry> sTotemCategoryStore("TotemCategory.db2", TotemCategoryFormat, HOTFIX_SEL_TOTEM_CATEGORY); +DB2Storage<ToyEntry> sToyStore("Toy.db2", ToyFormat, HOTFIX_SEL_TOY); DB2Storage<TransportAnimationEntry> sTransportAnimationStore("TransportAnimation.db2", TransportAnimationFormat, HOTFIX_SEL_TRANSPORT_ANIMATION); DB2Storage<TransportRotationEntry> sTransportRotationStore("TransportRotation.db2", TransportRotationFormat, HOTFIX_SEL_TRANSPORT_ROTATION); DB2Storage<UnitPowerBarEntry> sUnitPowerBarStore("UnitPowerBar.db2", UnitPowerBarFormat, HOTFIX_SEL_UNIT_POWER_BAR); @@ -273,6 +274,7 @@ void DB2Manager::LoadStores(std::string const& dataPath) LOAD_DB2(sTaxiPathNodeStore); LOAD_DB2(sTaxiPathStore); LOAD_DB2(sTotemCategoryStore); + LOAD_DB2(sToyStore); LOAD_DB2(sTransportAnimationStore); LOAD_DB2(sTransportRotationStore); LOAD_DB2(sUnitPowerBarStore); @@ -466,6 +468,9 @@ void DB2Manager::LoadStores(std::string const& dataPath) for (TransportRotationEntry const* rot : sTransportRotationStore) sTransportMgr->AddPathRotationToTransport(rot->TransportID, rot->TimeIndex, rot); + for (ToyEntry const* toy : sToyStore) + _toys.push_back(toy->ItemID); + // error checks if (bad_db2_files.size() >= DB2FilesCount) { @@ -804,3 +809,8 @@ bool DB2Manager::MountTypeXCapabilityEntryComparator::Compare(MountTypeXCapabili return left->OrderIndex < right->OrderIndex; return left->ID < right->ID; } + +bool DB2Manager::GetToyItemIdMatch(uint32 toy) const +{ + return std::find(_toys.begin(), _toys.end(), toy) != _toys.end(); +} diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 59931769c4a..98c36fe4b1d 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -92,6 +92,7 @@ extern DB2Storage<SpellXSpellVisualEntry> sSpellXSpellVisualStore; extern DB2Storage<TaxiNodesEntry> sTaxiNodesStore; extern DB2Storage<TaxiPathEntry> sTaxiPathStore; extern DB2Storage<TotemCategoryEntry> sTotemCategoryStore; +extern DB2Storage<ToyEntry> sToyStore; extern DB2Storage<UnitPowerBarEntry> sUnitPowerBarStore; extern DB2Storage<WorldMapOverlayEntry> sWorldMapOverlayStore; @@ -146,6 +147,7 @@ public: typedef std::unordered_map<uint32, std::vector<SpecializationSpellsEntry const*>> SpecializationSpellsContainer; typedef std::unordered_map<uint32, std::vector<SpellPowerEntry const*>> SpellPowerContainer; typedef std::unordered_map<uint32, std::unordered_map<uint32, std::vector<SpellPowerEntry const*>>> SpellPowerDifficultyContainer; + typedef std::vector<uint32> ToyItemIdsContainer; static DB2Manager& Instance() { @@ -179,6 +181,7 @@ public: std::set<uint32> GetPhasesForGroup(uint32 group) const; std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const; std::vector<SpellPowerEntry const*> GetSpellPowers(uint32 spellId, Difficulty difficulty = DIFFICULTY_NONE, bool* hasDifficultyPowers = nullptr) const; + bool GetToyItemIdMatch(uint32 toy) const; private: StorageMap _stores; @@ -202,6 +205,7 @@ private: SpecializationSpellsContainer _specializationSpellsBySpec; SpellPowerContainer _spellPowers; SpellPowerDifficultyContainer _spellPowerDifficulties; + ToyItemIdsContainer _toys; }; #define sDB2Manager DB2Manager::Instance() diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 0e5142e8bdf..b8bb0f2a94d 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1037,6 +1037,15 @@ struct TotemCategoryEntry uint32 CategoryMask; // 3 }; +struct ToyEntry +{ + uint32 ID; // 0 + uint32 ItemID; // 1 + uint32 Flags; // 2 + LocalizedString* Description; // 3 + uint32 CategoryFilter; // 4 +}; + struct TransportAnimationEntry { uint32 ID; // 0 diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h index 0bca88cf265..a9da1ae646a 100644 --- a/src/server/game/DataStores/DB2fmt.h +++ b/src/server/game/DataStores/DB2fmt.h @@ -106,6 +106,7 @@ char const TaxiNodesFormat[] = "nifffsiiiiiff"; char const TaxiPathFormat[] = "niii"; char const TaxiPathNodeFormat[] = "niiifffiiii"; char const TotemCategoryFormat[] = "nsii"; +char const ToyFormat[] = "niisi"; char const TransportAnimationFormat[] = "niifffi"; char const TransportRotationFormat[] = "niiffff"; char const UnitPowerBarFormat[] = "niiiiffiiiiiiiiiiiiiissssff"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8237a9759ef..9d64078cc8c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -86,6 +86,7 @@ #include "SpellMgr.h" #include "SpellPackets.h" #include "TalentPackets.h" +#include "ToyPackets.h" #include "TradePackets.h" #include "Transport.h" #include "UpdateData.h" @@ -17233,6 +17234,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); + _LoadToys(GetSession()->GetAccountToys()); LearnSpecializationSpells(); @@ -18371,6 +18373,23 @@ void Player::_LoadSpells(PreparedQueryResult result) } } +void Player::_LoadToys(ToyBoxContainer const& toys) +{ + for (auto const& t : toys) + AddDynamicValue(PLAYER_DYNAMIC_FIELD_TOYS, t.first); +} + +bool Player::AddToy(uint32 itemId, bool isFavourite /*= false*/) +{ + if (GetSession()->UpdateAccountToys(itemId, isFavourite)) + { + AddDynamicValue(PLAYER_DYNAMIC_FIELD_TOYS, itemId); + return true; + } + + return false; +} + void Player::_LoadGroup(PreparedQueryResult result) { //QueryResult* result = CharacterDatabase.PQuery("SELECT guid FROM group_member WHERE memberGuid=%u", GetGUIDLow()); @@ -19171,6 +19190,10 @@ void Player::SaveToDB(bool create /*=false*/) CharacterDatabase.CommitTransaction(trans); + SQLTransaction transLogin = LoginDatabase.BeginTransaction(); + GetSession()->SaveAccountToys(transLogin); + LoginDatabase.CommitTransaction(transLogin); + // save pet (hunter pet level and experience and all type pets health/mana). if (Pet* pet = GetPet()) pet->SavePetToDB(PET_SAVE_AS_CURRENT); @@ -22612,6 +22635,10 @@ void Player::SendInitialPacketsBeforeAddToMap() // SMSG_ACCOUNT_MOUNT_UPDATE // SMSG_ACCOUNT_TOYS_UPDATE + WorldPackets::Toy::AccountToysUpdate toysUpdate; + toysUpdate.IsFullUpdate = true; + toysUpdate.Toys = &GetSession()->GetAccountToys(); + SendDirectMessage(toysUpdate.Write()); WorldPackets::Character::InitialSetup initialSetup; initialSetup.ServerExpansionLevel = sWorld->getIntConfig(CONFIG_EXPANSION); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 83f60d64352..96c3be75a1c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2624,6 +2624,8 @@ class Player : public Unit, public GridObject<Player> VoidStorageItem* GetVoidStorageItem(uint8 slot) const; VoidStorageItem* GetVoidStorageItem(uint64 id, uint8& slot) const; + bool AddToy(uint32 itemId, bool isFavourite /*= false*/); + void OnCombatExit(); void CreateGarrison(uint32 garrSiteId); @@ -2701,6 +2703,7 @@ class Player : public Unit, public GridObject<Player> void _LoadGroup(PreparedQueryResult result); void _LoadSkills(PreparedQueryResult result); void _LoadSpells(PreparedQueryResult result); + void _LoadToys(ToyBoxContainer const& toys); void _LoadFriendList(PreparedQueryResult result); bool _LoadHomeBind(PreparedQueryResult result); void _LoadDeclinedNames(PreparedQueryResult result); diff --git a/src/server/game/Handlers/ToyHandler.cpp b/src/server/game/Handlers/ToyHandler.cpp new file mode 100644 index 00000000000..89ed9426c0f --- /dev/null +++ b/src/server/game/Handlers/ToyHandler.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Item.h" +#include "ToyPackets.h" +#include "WorldSession.h" + +void WorldSession::HandleAddToy(WorldPackets::Toy::AddToy& packet) +{ + if (!packet.Guid) + return; + + Item* item = _player->GetItemByGuid(packet.Guid); + if (!item) + { + _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); + return; + } + + if (!sDB2Manager.GetToyItemIdMatch(item->GetEntry())) + return; + + InventoryResult msg = _player->CanUseItem(item); + if (msg != EQUIP_ERR_OK) + { + _player->SendEquipError(msg, item, nullptr); + return; + } + + if (_player->AddToy(item->GetEntry(), false)) + _player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); +} + +void WorldSession::HandleUseToy(WorldPackets::Toy::UseToy& packet) +{ + ItemTemplate const* item = sObjectMgr->GetItemTemplate(packet.ItemID); + if (!item) + return; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.Cast.SpellID); + if (!spellInfo) + { + TC_LOG_ERROR("network", "HandleUseToy: unknown spell id: %u used by Toy Item entry %u", packet.Cast.SpellID, packet.ItemID); + return; + } + + if (_player->isPossessing()) + return; + + SpellCastTargets targets(_player, packet.Cast); + + Spell* spell = new Spell(_player, spellInfo, TRIGGERED_NONE, ObjectGuid::Empty, false); + spell->m_castItemEntry = packet.ItemID; + spell->m_cast_count = packet.Cast.CastID; + spell->m_misc.Raw.Data[0] = packet.Cast.Misc[0]; + spell->m_misc.Raw.Data[1] = packet.Cast.Misc[1]; + spell->m_castFlagsEx |= CAST_FLAG_EX_USE_TOY_SPELL; + spell->prepare(&targets); +} + +void WorldSession::HandleToySetFavorite(WorldPackets::Toy::ToySetFavorite& packet) +{ + ToyBoxContainer::iterator itr = _toys.find(packet.ItemID); + if (itr == _toys.end()) + return; + + itr->second = packet.Favorite; +} diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h index fbd53c0527c..9797f86585c 100644 --- a/src/server/game/Server/Packets/SpellPackets.h +++ b/src/server/game/Server/Packets/SpellPackets.h @@ -784,5 +784,6 @@ namespace WorldPackets } ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellCastLogData const& spellCastLogData); +ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::SpellCastRequest& request); #endif // SpellPackets_h__ diff --git a/src/server/game/Server/Packets/ToyPackets.cpp b/src/server/game/Server/Packets/ToyPackets.cpp new file mode 100644 index 00000000000..9d0f7b519b9 --- /dev/null +++ b/src/server/game/Server/Packets/ToyPackets.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ToyPackets.h" + +void WorldPackets::Toy::AddToy::Read() +{ + _worldPacket >> Guid; +} + +void WorldPackets::Toy::UseToy::Read() +{ + _worldPacket >> ItemID; + _worldPacket >> Cast; +} + +WorldPacket const* WorldPackets::Toy::AccountToysUpdate::Write() +{ + _worldPacket.WriteBit(IsFullUpdate); + _worldPacket.FlushBits(); + + // both lists have to have the same size + _worldPacket << int32(Toys->size()); + _worldPacket << int32(Toys->size()); + + for (auto const& item : *Toys) + _worldPacket << uint32(item.first); + + for (auto const& favourite : *Toys) + _worldPacket.WriteBit(favourite.second); + + _worldPacket.FlushBits(); + + return &_worldPacket; +} + +void WorldPackets::Toy::ToySetFavorite::Read() +{ + _worldPacket >> ItemID; + Favorite = _worldPacket.ReadBit(); +} diff --git a/src/server/game/Server/Packets/ToyPackets.h b/src/server/game/Server/Packets/ToyPackets.h new file mode 100644 index 00000000000..11b0f47ddda --- /dev/null +++ b/src/server/game/Server/Packets/ToyPackets.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ToyPackets_h__ +#define ToyPackets_h__ + +#include "Packet.h" +#include "ObjectGuid.h" +#include "SpellPackets.h" + +namespace WorldPackets +{ + namespace Toy + { + class AddToy final : public ClientPacket + { + public: + AddToy(WorldPacket&& packet) : ClientPacket(CMSG_ADD_TOY, std::move(packet)) { } + + void Read() override; + + ObjectGuid Guid; + }; + + class UseToy final : public ClientPacket + { + public: + UseToy(WorldPacket&& packet) : ClientPacket(CMSG_USE_TOY, std::move(packet)) { } + + void Read() override; + + WorldPackets::Spells::SpellCastRequest Cast; + uint32 ItemID = 0; + }; + + class AccountToysUpdate final : public ServerPacket + { + public: + AccountToysUpdate() : ServerPacket(SMSG_ACCOUNT_TOYS_UPDATE, 1 + 4 + 1) { } + + WorldPacket const* Write() override; + + bool IsFullUpdate = false; + ToyBoxContainer const* Toys = nullptr; + }; + + class ToySetFavorite final : public ClientPacket + { + public: + ToySetFavorite(WorldPacket&& packet) : ClientPacket(CMSG_TOY_SET_FAVORITE, std::move(packet)) { } + + void Read() override; + + uint32 ItemID = 0; + bool Favorite = false; + }; + } +} + +#endif // ToyPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 03a48630afc..24a01eb9057 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -54,6 +54,7 @@ #include "Packets/TicketPackets.h" #include "Packets/TokenPackets.h" #include "Packets/TotemPackets.h" +#include "Packets/ToyPackets.h" #include "Packets/TradePackets.h" #include "Packets/VehiclePackets.h" #include "Packets/VoidStoragePackets.h" @@ -163,7 +164,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_ADD_BATTLENET_FRIEND, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_ADD_FRIEND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Social::AddFriend, &WorldSession::HandleAddFriendOpcode); DEFINE_HANDLER(CMSG_ADD_IGNORE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Social::AddIgnore, &WorldSession::HandleAddIgnoreOpcode); - DEFINE_HANDLER(CMSG_ADD_TOY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_ADD_TOY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Toy::AddToy, &WorldSession::HandleAddToy); DEFINE_HANDLER(CMSG_ALTER_APPEARANCE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::AlterApperance, &WorldSession::HandleAlterAppearance); DEFINE_HANDLER(CMSG_AREA_SPIRIT_HEALER_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Battleground::AreaSpiritHealerQuery, &WorldSession::HandleAreaSpiritHealerQueryOpcode); DEFINE_HANDLER(CMSG_AREA_SPIRIT_HEALER_QUEUE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Battleground::AreaSpiritHealerQueue, &WorldSession::HandleAreaSpiritHealerQueueOpcode); @@ -786,7 +787,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_TOGGLE_DIFFICULTY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_TOGGLE_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::TogglePvP, &WorldSession::HandleTogglePvP); DEFINE_HANDLER(CMSG_TOTEM_DESTROYED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Totem::TotemDestroyed, &WorldSession::HandleTotemDestroyed); - DEFINE_HANDLER(CMSG_TOY_SET_FAVORITE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_TOY_SET_FAVORITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Toy::ToySetFavorite, &WorldSession::HandleToySetFavorite); DEFINE_HANDLER(CMSG_TRAINER_BUY_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::TrainerBuySpell, &WorldSession::HandleTrainerBuySpellOpcode); DEFINE_HANDLER(CMSG_TRAINER_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleTrainerListOpcode); DEFINE_HANDLER(CMSG_TRANSMOGRIFY_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::TransmogrifyItems, &WorldSession::HandleTransmogrifyItems); @@ -815,7 +816,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_USE_CRITTER_ITEM, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_USE_EQUIPMENT_SET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::EquipmentSet::UseEquipmentSet, &WorldSession::HandleUseEquipmentSet); DEFINE_HANDLER(CMSG_USE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::UseItem, &WorldSession::HandleUseItemOpcode); - DEFINE_HANDLER(CMSG_USE_TOY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_USE_TOY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Toy::UseToy, &WorldSession::HandleUseToy); DEFINE_HANDLER(CMSG_VIOLENCE_LEVEL, STATUS_AUTHED, PROCESS_INPLACE, WorldPackets::Misc::ViolenceLevel, &WorldSession::HandleViolenceLevel); DEFINE_HANDLER(CMSG_VOICE_ADD_IGNORE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_VOICE_DEL_IGNORE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); @@ -840,7 +841,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_DATA_TIMES, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_HEIRLOOM_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_MOUNT_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TOYS_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TOYS_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_DELETED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_EARNED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTIVATE_TAXI_REPLY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index bc5b2ef69b6..b54528ee248 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -748,6 +748,40 @@ void WorldSession::LoadAccountData(PreparedQueryResult result, uint32 mask) while (result->NextRow()); } +void WorldSession::LoadAccountToys(PreparedQueryResult result) +{ + if (!result) + return; + + do + { + Field* fields = result->Fetch(); + uint32 itemId = fields[0].GetUInt32(); + bool isFavourite = fields[1].GetBool(); + + _toys[itemId] = isFavourite; + } + while (result->NextRow()); +} + +void WorldSession::SaveAccountToys(SQLTransaction& trans) +{ + PreparedStatement* stmt = NULL; + for (ToyBoxContainer::const_iterator itr = _toys.begin(); itr != _toys.end(); ++itr) + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_ACCOUNT_TOYS); + stmt->setUInt32(0, GetBattlenetAccountId()); + stmt->setUInt32(1, itr->first); + stmt->setBool(2, itr->second); + trans->Append(stmt); + } +} + +bool WorldSession::UpdateAccountToys(uint32 itemId, bool isFavourite /*= false*/) +{ + return _toys.insert(ToyBoxContainer::value_type(itemId, isFavourite)).second; +} + void WorldSession::SetAccountData(AccountDataType type, uint32 time, std::string const& data) { if ((1 << type) & GLOBAL_CACHE_MASK) @@ -1156,15 +1190,21 @@ class AccountInfoQueryHolder : public SQLQueryHolder public: enum { + GLOBAL_ACCOUNT_TOYS = 0, + MAX_QUERIES }; AccountInfoQueryHolder() { SetSize(MAX_QUERIES); } - bool Initialize(uint32 /*accountId*/, uint32 /*battlenetAccountId*/) + bool Initialize(uint32 /*accountId*/, uint32 battlenetAccountId) { bool ok = true; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_TOYS); + stmt->setUInt32(0, battlenetAccountId); + ok = SetPreparedQuery(GLOBAL_ACCOUNT_TOYS, stmt) && ok; + return ok; } }; @@ -1196,6 +1236,7 @@ void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQue { LoadAccountData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::GLOBAL_ACCOUNT_DATA), GLOBAL_CACHE_MASK); LoadTutorialsData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::TUTORIALS)); + LoadAccountToys(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_TOYS)); if (!m_inQueue) SendAuthResponse(AUTH_OK, false); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 107a4350af6..c759fe0a98a 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -494,6 +494,14 @@ namespace WorldPackets class RequestForcedReactions; } + namespace Toy + { + class AccountToysUpdate; + class AddToy; + class ToySetFavorite; + class UseToy; + } + namespace Scenes { class SceneTriggerEvent; @@ -662,6 +670,8 @@ enum Tutorials #define MAX_ACCOUNT_TUTORIAL_VALUES 8 +typedef std::map<uint32, bool> ToyBoxContainer; + struct AccountData { time_t Time = 0; @@ -885,6 +895,12 @@ class WorldSession void SetAccountData(AccountDataType type, uint32 time, std::string const& data); void LoadAccountData(PreparedQueryResult result, uint32 mask); + // Account Toys + void LoadAccountToys(PreparedQueryResult result); + void SaveAccountToys(SQLTransaction& trans); + bool UpdateAccountToys(uint32 itemId, bool isFavourite /*= false*/); + ToyBoxContainer const& GetAccountToys() const { return _toys; } + void LoadTutorialsData(PreparedQueryResult result); void SendTutorialsData(); void SaveTutorialsData(SQLTransaction& trans); @@ -1540,6 +1556,11 @@ class WorldSession void HandleObjectUpdateRescuedOpcode(WorldPackets::Misc::ObjectUpdateRescued& objectUpdateRescued); void HandleRequestCategoryCooldowns(WorldPackets::Spells::RequestCategoryCooldowns& requestCategoryCooldowns); + // Toys + void HandleAddToy(WorldPackets::Toy::AddToy& packet); + void HandleToySetFavorite(WorldPackets::Toy::ToySetFavorite& packet); + void HandleUseToy(WorldPackets::Toy::UseToy& packet); + // Scenes void HandleSceneTriggerEvent(WorldPackets::Scenes::SceneTriggerEvent& sceneTriggerEvent); void HandleScenePlaybackComplete(WorldPackets::Scenes::ScenePlaybackComplete& scenePlaybackComplete); @@ -1671,6 +1692,7 @@ class WorldSession uint32 expireTime; bool forceExit; ObjectGuid m_currentBankerGUID; + ToyBoxContainer _toys; WorldSession(WorldSession const& right) = delete; WorldSession& operator=(WorldSession const& right) = delete; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 97250169a8a..632392ada32 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -569,6 +569,7 @@ m_spellValue(new SpellValue(caster->GetMap()->GetDifficultyID(), m_spellInfo)), m_CastItem = NULL; m_castItemGUID.Clear(); m_castItemEntry = 0; + m_castFlagsEx = 0; unitTarget = NULL; itemTarget = NULL; @@ -2858,11 +2859,6 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered m_castItemGUID = m_CastItem->GetGUID(); m_castItemEntry = m_CastItem->GetEntry(); } - else - { - m_castItemGUID.Clear(); - m_castItemEntry = 0; - } InitExplicitTargets(*targets); @@ -3477,7 +3473,10 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + if (m_CastItem) + m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + else + m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_castItemEntry, this); } void Spell::update(uint32 difftime) @@ -3836,6 +3835,7 @@ void Spell::SendSpellStart() castData.SpellID = m_spellInfo->Id; castData.SpellXSpellVisualID = m_SpellVisual; castData.CastFlags = castFlags; + castData.CastFlagsEx = m_castFlagsEx; castData.CastTime = m_casttime; m_targets.Write(castData.Target); @@ -3952,6 +3952,7 @@ void Spell::SendSpellGo() castData.SpellID = m_spellInfo->Id; castData.SpellXSpellVisualID = m_SpellVisual; castData.CastFlags = castFlags; + castData.CastFlagsEx = m_castFlagsEx; castData.CastTime = getMSTime(); UpdateSpellCastDataTargets(castData); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index fe4b193c07d..c894c53aaf2 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -83,6 +83,31 @@ enum SpellCastFlags CAST_FLAG_UNKNOWN_32 = 0x80000000 }; +enum SpellCastFlagsEx +{ + CAST_FLAG_EX_NONE = 0x00000, + CAST_FLAG_EX_UNKNOWN_1 = 0x00001, + CAST_FLAG_EX_UNKNOWN_2 = 0x00002, + CAST_FLAG_EX_UNKNOWN_3 = 0x00004, + CAST_FLAG_EX_UNKNOWN_4 = 0x00008, + CAST_FLAG_EX_UNKNOWN_5 = 0x00010, + CAST_FLAG_EX_UNKNOWN_6 = 0x00020, + CAST_FLAG_EX_UNKNOWN_7 = 0x00040, + CAST_FLAG_EX_UNKNOWN_8 = 0x00080, + CAST_FLAG_EX_UNKNOWN_9 = 0x00100, + CAST_FLAG_EX_UNKNOWN_10 = 0x00200, + CAST_FLAG_EX_UNKNOWN_11 = 0x00400, + CAST_FLAG_EX_UNKNOWN_12 = 0x00800, + CAST_FLAG_EX_UNKNOWN_13 = 0x01000, + CAST_FLAG_EX_UNKNOWN_14 = 0x02000, + CAST_FLAG_EX_UNKNOWN_15 = 0x04000, + CAST_FLAG_EX_USE_TOY_SPELL = 0x08000, // Starts cooldown on toy + CAST_FLAG_EX_UNKNOWN_17 = 0x10000, + CAST_FLAG_EX_UNKNOWN_18 = 0x20000, + CAST_FLAG_EX_UNKNOWN_19 = 0x40000, + CAST_FLAG_EX_UNKNOWN_20 = 0x80000 +}; + enum SpellRangeFlag { SPELL_RANGE_DEFAULT = 0, @@ -519,6 +544,7 @@ class Spell ObjectGuid m_castItemGUID; uint32 m_castItemEntry; uint8 m_cast_count; + uint32 m_castFlagsEx; union { // Alternate names for this value diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 64902febc77..13aa7b39b03 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -209,23 +209,31 @@ void SpellHistory::Update() void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/) { + HandleCooldowns(spellInfo, item ? item->GetEntry() : 0, spell); +} + +void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 ItemID, Spell* spell /*= nullptr*/) +{ if (ConsumeCharge(spellInfo->ChargeCategoryEntry)) return; if (Player* player = _owner->ToPlayer()) { // potions start cooldown until exiting combat - if (item && (item->IsPotion() || spellInfo->IsCooldownStartedOnEvent())) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(ItemID)) { - player->SetLastPotionId(item->GetEntry()); - return; + if (itemTemplate->IsPotion() || spellInfo->IsCooldownStartedOnEvent()) + { + player->SetLastPotionId(ItemID); + return; + } } } if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive() || (spell && spell->IsIgnoringCooldowns())) return; - StartCooldown(spellInfo, item ? item->GetEntry() : 0, spell); + StartCooldown(spellInfo, ItemID, spell); } bool SpellHistory::IsReady(SpellInfo const* spellInfo) const diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index a55f57aa9f4..9f269dfee73 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -79,6 +79,7 @@ public: void Update(); void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr); + void HandleCooldowns(SpellInfo const* spellInfo, uint32 ItemID, Spell* spell = nullptr); bool IsReady(SpellInfo const* spellInfo) const; template<class PacketType> void WritePacket(PacketType* packet) const; |