aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMitchesD <majklprofik@seznam.cz>2015-08-26 11:12:35 +0200
committerMitchesD <majklprofik@seznam.cz>2015-08-26 11:15:00 +0200
commit614b5832ba96b4c5905ece5490a7b5d18c2f710b (patch)
tree454e71dfcab1da90490466dfebd69a115950866d /src
parentca5c5221ba8376d152a4c8216afd922924475802 (diff)
Core/Player: implemented Toybox
* now you can use all the things in toybox - add new toys, set them as favorite and use them
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h3
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h3
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp10
-rw-r--r--src/server/game/DataStores/DB2Stores.h4
-rw-r--r--src/server/game/DataStores/DB2Structure.h9
-rw-r--r--src/server/game/DataStores/DB2fmt.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp27
-rw-r--r--src/server/game/Entities/Player/Player.h3
-rw-r--r--src/server/game/Handlers/ToyHandler.cpp82
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h1
-rw-r--r--src/server/game/Server/Packets/ToyPackets.cpp55
-rw-r--r--src/server/game/Server/Packets/ToyPackets.h74
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp9
-rw-r--r--src/server/game/Server/WorldSession.cpp43
-rw-r--r--src/server/game/Server/WorldSession.h22
-rw-r--r--src/server/game/Spells/Spell.cpp13
-rw-r--r--src/server/game/Spells/Spell.h26
-rw-r--r--src/server/game/Spells/SpellHistory.cpp16
-rw-r--r--src/server/game/Spells/SpellHistory.h1
21 files changed, 395 insertions, 15 deletions
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;