aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorhorn <pankrac.ja@seznam.cz>2015-09-09 14:52:32 +0200
committerhorn <pankrac.ja@seznam.cz>2015-09-09 14:52:32 +0200
commite8b1faa1562c85a9e7d63af68d750cd574c0e396 (patch)
treebedeb266d1114666983e6b7ce947e87d65446308 /src/server
parente0fcb410b4d08119b7e72f154d7eed8cd2bd1e0f (diff)
Core/BattlePets: Basics for Battle Pets
Diffstat (limited to 'src/server')
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp6
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp17
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h11
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp9
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h8
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp10
-rw-r--r--src/server/game/BattlePets/BattlePetMgr.cpp467
-rw-r--r--src/server/game/BattlePets/BattlePetMgr.h141
-rw-r--r--src/server/game/CMakeLists.txt3
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp10
-rw-r--r--src/server/game/DataStores/DB2Stores.h5
-rw-r--r--src/server/game/DataStores/DB2Structure.h42
-rw-r--r--src/server/game/DataStores/DB2fmt.h5
-rw-r--r--src/server/game/DataStores/DBCEnums.h1
-rw-r--r--src/server/game/Entities/Item/Item.cpp16
-rw-r--r--src/server/game/Entities/Player/Player.cpp29
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Guilds/Guild.cpp4
-rw-r--r--src/server/game/Guilds/GuildMgr.cpp10
-rw-r--r--src/server/game/Handlers/BattlePetHandler.cpp80
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp5
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp19
-rw-r--r--src/server/game/Server/Packets/BattlePetPackets.cpp171
-rw-r--r--src/server/game/Server/Packets/BattlePetPackets.h194
-rw-r--r--src/server/game/Server/Packets/ItemPackets.cpp5
-rw-r--r--src/server/game/Server/Packets/ItemPackets.h10
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp29
-rw-r--r--src/server/game/Server/WorldSession.cpp17
-rw-r--r--src/server/game/Server/WorldSession.h28
-rw-r--r--src/server/game/Spells/Spell.h3
-rw-r--r--src/server/game/Spells/SpellEffects.cpp105
-rw-r--r--src/server/game/World/World.cpp4
32 files changed, 1420 insertions, 46 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index f79fb8faf41..2de1dc0645b 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -22,7 +22,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
if (!m_reconnecting)
m_stmts.resize(MAX_CHARACTERDATABASE_STATEMENTS);
-#define SelectItemInstanceContent "ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId, ii.enchantIllusion, ii.bonusListIDs"
+#define SelectItemInstanceContent "ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId, ii.enchantIllusion, ii.battlePetSpeciesId, ii.battlePetBreedData, ii.battlePetLevel, ii.battlePetDisplayId, ii.bonusListIDs"
PrepareStatement(CHAR_DEL_QUEST_POOL_SAVE, "DELETE FROM pool_quest_save WHERE pool_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_QUEST_POOL_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC);
@@ -159,8 +159,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_ITEM_BOP_TRADE, "DELETE FROM item_soulbound_trade_data WHERE itemGuid = ? LIMIT 1", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_ITEM_BOP_TRADE, "INSERT INTO item_soulbound_trade_data VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_INVENTORY_ITEM, "REPLACE INTO character_inventory (guid, bag, slot, item) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion, bonusListIDs, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ?, transmogrification = ?, upgradeId = ?, enchantIllusion = ?, bonusListIDs = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ?, transmogrification = ?, upgradeId = ?, enchantIllusion = ?, battlePetSpeciesId = ?, battlePetBreedData = ?, battlePetLevel = ?, battlePetDisplayId = ?, bonusListIDs = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD, "UPDATE item_instance SET duration = ?, flags = ?, durability = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE, "DELETE FROM item_instance WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER, "DELETE FROM item_instance WHERE owner_guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp
index f2eb88589e1..104b5e2bbc7 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -47,6 +47,20 @@ void HotfixDatabaseConnection::DoPrepareStatements()
" ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_BARBER_SHOP_STYLE, "SELECT ID, DisplayName_lang, Description_lang FROM barber_shop_style_locale WHERE locale = ?", CONNECTION_SYNCH);
+ // BattlePetBreedQuality.db2
+ PrepareStatement(HOTFIX_SEL_BATTLE_PET_BREED_QUALITY, "SELECT ID, Quality, Modifier FROM battle_pet_breed_quality ORDER BY ID DESC", CONNECTION_SYNCH);
+
+ // BattlePetBreedState.db2
+ PrepareStatement(HOTFIX_SEL_BATTLE_PET_BREED_STATE, "SELECT ID, BreedID, State, Value FROM battle_pet_breed_state ORDER BY ID DESC", CONNECTION_SYNCH);
+
+ // BattlePetSpecies.db2
+ PrepareStatement(HOTFIX_SEL_BATTLE_PET_SPECIES, "SELECT ID, CreatureID, IconFileID, SummonSpellID, PetType, Source, Flags, "
+ "SourceText, Description FROM battle_pet_species ORDER BY ID DESC", CONNECTION_SYNCH);
+ PREPARE_LOCALE_STMT(HOTFIX_SEL_BATTLE_PET_SPECIES, "SELECT ID, SourceText, Description FROM battle_pet_species_locale WHERE locale = ?", CONNECTION_SYNCH);
+
+ // BattlePetSpeciesState.db2
+ PrepareStatement(HOTFIX_SEL_BATTLE_PET_SPECIES_STATE, "SELECT ID, SpeciesID, State, Value FROM battle_pet_species_state ORDER BY ID DESC", CONNECTION_SYNCH);
+
// BroadcastText.db2
PrepareStatement(HOTFIX_SEL_BROADCAST_TEXT, "SELECT ID, Language, MaleText, FemaleText, EmoteID1, EmoteID2, EmoteID3, EmoteDelay1, EmoteDelay2, "
"EmoteDelay3, SoundID, UnkEmoteID, Type FROM broadcast_text ORDER BY ID DESC", CONNECTION_SYNCH);
@@ -277,6 +291,9 @@ void HotfixDatabaseConnection::DoPrepareStatements()
// ItemSpecOverride.db2
PrepareStatement(HOTFIX_SEL_ITEM_SPEC_OVERRIDE, "SELECT ID, ItemID, SpecID FROM item_spec_override ORDER BY ID DESC", CONNECTION_SYNCH);
+ // ItemToBattlePetSpecies.db2
+ PrepareStatement(HOTFIX_SEL_ITEM_TO_BATTLE_PET_SPECIES, "SELECT ID, BattlePetSpeciesID FROM item_to_battle_pet_species ORDER BY ID DESC", CONNECTION_SYNCH);
+
// ItemXBonusTree.db2
PrepareStatement(HOTFIX_SEL_ITEM_X_BONUS_TREE, "SELECT ID, ItemID, BonusTreeID FROM item_x_bonus_tree 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 cbff13a4fbf..196d7602da7 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -42,6 +42,15 @@ enum HotfixDatabaseStatements
HOTFIX_SEL_BARBER_SHOP_STYLE,
HOTFIX_SEL_BARBER_SHOP_STYLE_LOCALE,
+ HOTFIX_SEL_BATTLE_PET_BREED_QUALITY,
+
+ HOTFIX_SEL_BATTLE_PET_BREED_STATE,
+
+ HOTFIX_SEL_BATTLE_PET_SPECIES,
+ HOTFIX_SEL_BATTLE_PET_SPECIES_LOCALE,
+
+ HOTFIX_SEL_BATTLE_PET_SPECIES_STATE,
+
HOTFIX_SEL_BROADCAST_TEXT,
HOTFIX_SEL_BROADCAST_TEXT_LOCALE,
@@ -159,6 +168,8 @@ enum HotfixDatabaseStatements
HOTFIX_SEL_ITEM_SPEC_OVERRIDE,
+ HOTFIX_SEL_ITEM_TO_BATTLE_PET_SPECIES,
+
HOTFIX_SEL_ITEM_X_BONUS_TREE,
HOTFIX_SEL_KEY_CHAIN,
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index 869dc617246..9526e012fa2 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -144,4 +144,13 @@ void LoginDatabaseConnection::DoPrepareStatements()
// 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);
+
+ // Battle Pets
+ PrepareStatement(LOGIN_SEL_BATTLE_PETS, "SELECT guid, species, breed, level, exp, health, quality, flags, name FROM battle_pets WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_BATTLE_PETS, "INSERT INTO battle_pets (guid, battlenetAccountId, species, breed, level, exp, health, quality, flags, name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_BATTLE_PETS, "DELETE FROM battle_pets WHERE battlenetAccountId = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_BATTLE_PETS, "UPDATE battle_pets SET level = ?, exp = ?, health = ?, quality = ?, flags = ?, name = ? WHERE battlenetAccountId = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_BATTLE_PET_SLOTS, "SELECT id, battlePetGuid, locked FROM battle_pet_slots WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_BATTLE_PET_SLOTS, "INSERT INTO battle_pet_slots (id, battlenetAccountId, battlePetGuid, locked) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_BATTLE_PET_SLOTS, "DELETE FROM battle_pet_slots WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
}
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index cd66be205f5..9ef214a3120 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -133,6 +133,14 @@ enum LoginDatabaseStatements
LOGIN_SEL_ACCOUNT_TOYS,
LOGIN_REP_ACCOUNT_TOYS,
+ LOGIN_SEL_BATTLE_PETS,
+ LOGIN_INS_BATTLE_PETS,
+ LOGIN_DEL_BATTLE_PETS,
+ LOGIN_UPD_BATTLE_PETS,
+ LOGIN_SEL_BATTLE_PET_SLOTS,
+ LOGIN_INS_BATTLE_PET_SLOTS,
+ LOGIN_DEL_BATTLE_PET_SLOTS,
+
MAX_LOGINDATABASE_STATEMENTS
};
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index 68d76272e22..91d97746ae1 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -1057,6 +1057,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: // This also behaves like ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
+ case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT:
SetCriteriaProgress(achievementCriteria, 1, referencePlayer, PROGRESS_ACCUMULATE);
break;
// std case: increment at miscValue1
@@ -1164,6 +1165,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER:
+ case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET:
SetCriteriaProgress(achievementCriteria, 1, referencePlayer);
break;
case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
@@ -1280,8 +1282,6 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
case ACHIEVEMENT_CRITERIA_TYPE_COUNT_OF_LFR_QUEUE_BOOSTS_BY_TANK:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_SCENARIO_COUNT:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_SCENARIO:
- case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET:
- case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT:
case ACHIEVEMENT_CRITERIA_TYPE_CAPTURE_BATTLE_PET:
case ACHIEVEMENT_CRITERIA_TYPE_WIN_PET_BATTLE:
case ACHIEVEMENT_CRITERIA_TYPE_LEVEL_BATTLE_PET:
@@ -1444,12 +1444,14 @@ bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteria const* achieveme
case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY:
case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
+ case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT:
return progress->counter >= requiredAmount;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER:
+ case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET:
return progress->counter >= 1;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
return progress->counter >= (requiredAmount * 75);
@@ -2533,6 +2535,10 @@ bool AchievementMgr<T>::AdditionalRequirementsSatisfied(ModifierTreeNode const*
if (!unit || unit->GetHealthPct() >= reqValue)
return false;
break;
+ case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_BATTLE_PET_SPECIES: // 91
+ if (miscValue1 != reqValue)
+ return false;
+ break;
case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_FOLLOWER_QUALITY: // 145
{
if (!referencePlayer)
diff --git a/src/server/game/BattlePets/BattlePetMgr.cpp b/src/server/game/BattlePets/BattlePetMgr.cpp
new file mode 100644
index 00000000000..b65302f4050
--- /dev/null
+++ b/src/server/game/BattlePets/BattlePetMgr.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 "BattlePetMgr.h"
+#include "Containers.h"
+#include "Player.h"
+#include "WorldSession.h"
+
+void BattlePetMgr::BattlePet::CalculateStats()
+{
+ float health = 0.0f;
+ float power = 0.0f;
+ float speed = 0.0f;
+
+ // get base breed stats
+ auto breedState = _battlePetBreedStates.find(PacketInfo.Breed);
+ if (breedState == _battlePetBreedStates.end()) // non existing breed id
+ return;
+
+ health = breedState->second[STATE_STAT_STAMINA];
+ power = breedState->second[STATE_STAT_POWER];
+ speed = breedState->second[STATE_STAT_SPEED];
+
+ // modify stats depending on species - not all pets have this
+ auto speciesState = _battlePetSpeciesStates.find(PacketInfo.Species);
+ if (speciesState != _battlePetSpeciesStates.end())
+ {
+ health += speciesState->second[STATE_STAT_STAMINA];
+ power += speciesState->second[STATE_STAT_POWER];
+ speed += speciesState->second[STATE_STAT_SPEED];
+ }
+
+ // modify stats by quality
+ for (auto itr : sBattlePetBreedQualityStore)
+ {
+ if (itr->Quality == PacketInfo.Quality)
+ {
+ health *= itr->Modifier;
+ power *= itr->Modifier;
+ speed *= itr->Modifier;
+ break;
+ }
+ // TOOD: add check if pet has existing quality
+ }
+
+ // scale stats depending on level
+ health *= PacketInfo.Level;
+ power *= PacketInfo.Level;
+ speed *= PacketInfo.Level;
+
+ // set stats
+ // round, ceil or floor? verify this
+ PacketInfo.MaxHealth = uint32((round(health / 20) + 100));
+ PacketInfo.Power = uint32(round(power / 100));
+ PacketInfo.Speed = uint32(round(speed / 100));
+}
+
+std::unordered_map<uint16 /*BreedID*/, std::unordered_map<BattlePetState /*state*/, int32 /*value*/, std::hash<std::underlying_type<BattlePetState>::type> >> BattlePetMgr::_battlePetBreedStates;
+std::unordered_map<uint32 /*SpeciesID*/, std::unordered_map<BattlePetState /*state*/, int32 /*value*/, std::hash<std::underlying_type<BattlePetState>::type> >> BattlePetMgr::_battlePetSpeciesStates;
+std::unordered_map<uint32 /*SpeciesID*/, std::unordered_set<uint8 /*breed*/>> BattlePetMgr::_availableBreedsPerSpecies;
+std::unordered_map<uint32 /*SpeciesID*/, uint8 /*quality*/> BattlePetMgr::_defaultQualityPerSpecies;
+
+void BattlePetMgr::Initialize()
+{
+ if (QueryResult result = LoginDatabase.Query("SELECT MAX(guid) FROM battle_pets"))
+ sObjectMgr->GetGenerator<HighGuid::BattlePet>().Set((*result)[0].GetUInt64() + 1);
+
+ for (auto itr : sBattlePetBreedStateStore)
+ _battlePetBreedStates[itr->BreedID][BattlePetState(itr->State)] = itr->Value;
+
+ for (auto itr : sBattlePetSpeciesStateStore)
+ _battlePetSpeciesStates[itr->SpeciesID][BattlePetState(itr->State)] = itr->Value;
+
+ LoadAvailablePetBreeds();
+ LoadDefaultPetQualities();
+}
+
+void BattlePetMgr::LoadAvailablePetBreeds()
+{
+ QueryResult result = WorldDatabase.Query("SELECT speciesId, breedId FROM battle_pet_breeds");
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 battle pet breeds. DB table `battle_pet_breeds` is empty.");
+ return;
+ }
+
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 speciesId = fields[0].GetUInt32();
+ uint16 breedId = fields[1].GetUInt16();
+
+ if (!sBattlePetSpeciesStore.LookupEntry(speciesId))
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing BattlePetSpecies.db2 entry %u was referenced in `battle_pet_breeds` by row (%u, %u).", speciesId, speciesId, breedId);
+ continue;
+ }
+
+ // TODO: verify breed id (3 - 12 (male) or 3 - 22 (male and female)) if needed
+
+ _availableBreedsPerSpecies[speciesId].insert(breedId);
+ ++count;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u battle pet breeds.", count);
+}
+
+void BattlePetMgr::LoadDefaultPetQualities()
+{
+ QueryResult result = WorldDatabase.Query("SELECT speciesId, quality FROM battle_pet_quality");
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 battle pet qualities. DB table `battle_pet_quality` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 speciesId = fields[0].GetUInt32();
+ uint8 quality = fields[1].GetUInt8();
+
+ if (!sBattlePetSpeciesStore.LookupEntry(speciesId))
+ {
+ TC_LOG_ERROR("sql.sql", "Non-existing BattlePetSpecies.db2 entry %u was referenced in `battle_pet_quality` by row (%u, %u).", speciesId, speciesId, quality);
+ continue;
+ }
+
+ // TODO: verify quality (0 - 3 for player pets or 0 - 5 for both player and tamer pets) if needed
+
+ _defaultQualityPerSpecies[speciesId] = quality;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u battle pet qualities.", uint32(_defaultQualityPerSpecies.size()));
+}
+
+uint16 BattlePetMgr::RollPetBreed(uint32 species)
+{
+ auto itr = _availableBreedsPerSpecies.find(species);
+ if (itr == _availableBreedsPerSpecies.end())
+ return 3; // default B/B
+
+ return Trinity::Containers::SelectRandomContainerElement(itr->second);
+}
+
+uint8 BattlePetMgr::GetDefaultPetQuality(uint32 species)
+{
+ auto itr = _defaultQualityPerSpecies.find(species);
+ if (itr == _defaultQualityPerSpecies.end())
+ return 0; // default poor
+
+ return itr->second;
+}
+
+BattlePetMgr::BattlePetMgr(WorldSession* owner)
+{
+ _owner = owner;
+ for (uint8 i = 0; i < MAX_PET_BATTLE_SLOTS; ++i)
+ {
+ WorldPackets::BattlePet::BattlePetSlot slot;
+ slot.Index = i;
+ _slots.push_back(slot);
+ }
+}
+
+void BattlePetMgr::LoadFromDB(PreparedQueryResult pets, PreparedQueryResult slots)
+{
+ if (pets)
+ {
+ do
+ {
+ Field* fields = pets->Fetch();
+ uint32 species = fields[1].GetUInt32();
+
+ if (BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(species))
+ {
+ if (GetPetCount(species) >= MAX_BATTLE_PETS_PER_SPECIES)
+ {
+ TC_LOG_ERROR("misc", "Battlenet account with id %u has more than 3 battle pets of species %u", _owner->GetBattlenetAccountId(), species);
+ continue;
+ }
+
+ BattlePet pet;
+ pet.PacketInfo.Guid = ObjectGuid::Create<HighGuid::BattlePet>(fields[0].GetUInt64());
+ pet.PacketInfo.Species = species;
+ pet.PacketInfo.Breed = fields[2].GetUInt16();
+ pet.PacketInfo.Level = fields[3].GetUInt16();
+ pet.PacketInfo.Exp = fields[4].GetUInt16();
+ pet.PacketInfo.Health = fields[5].GetUInt32();
+ pet.PacketInfo.Quality = fields[6].GetUInt8();
+ pet.PacketInfo.Flags = fields[7].GetUInt16();
+ pet.PacketInfo.Name = fields[8].GetString();
+ pet.PacketInfo.CreatureID = speciesEntry->CreatureID;
+ pet.SaveInfo = BATTLE_PET_UNCHANGED;
+ pet.CalculateStats();
+ _pets[pet.PacketInfo.Guid.GetCounter()] = pet;
+ }
+ } while (pets->NextRow());
+ }
+
+ if (slots)
+ {
+ uint8 i = 0; // slots->GetRowCount() should equal MAX_BATTLE_PET_SLOTS
+
+ do
+ {
+ Field* fields = slots->Fetch();
+ _slots[i].Index = fields[0].GetUInt8();
+ auto itr = _pets.find(fields[1].GetUInt64());
+ if (itr != _pets.end())
+ _slots[i].Pet = itr->second.PacketInfo;
+ _slots[i].Locked = fields[2].GetBool();
+ i++;
+ } while (slots->NextRow());
+ }
+}
+
+void BattlePetMgr::SaveToDB(SQLTransaction& trans)
+{
+ PreparedStatement* stmt = nullptr;
+
+ for (auto itr = _pets.begin(); itr != _pets.end();)
+ {
+ switch (itr->second.SaveInfo)
+ {
+ case BATTLE_PET_NEW:
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BATTLE_PETS);
+ stmt->setUInt64(0, itr->first);
+ stmt->setUInt32(1, _owner->GetBattlenetAccountId());
+ stmt->setUInt32(2, itr->second.PacketInfo.Species);
+ stmt->setUInt16(3, itr->second.PacketInfo.Breed);
+ stmt->setUInt16(4, itr->second.PacketInfo.Level);
+ stmt->setUInt16(5, itr->second.PacketInfo.Exp);
+ stmt->setUInt32(6, itr->second.PacketInfo.Health);
+ stmt->setUInt8(7, itr->second.PacketInfo.Quality);
+ stmt->setUInt16(8, itr->second.PacketInfo.Flags);
+ stmt->setString(9, itr->second.PacketInfo.Name);
+ trans->Append(stmt);
+ itr->second.SaveInfo = BATTLE_PET_UNCHANGED;
+ ++itr;
+ break;
+ case BATTLE_PET_CHANGED:
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BATTLE_PETS);
+ stmt->setUInt16(0, itr->second.PacketInfo.Level);
+ stmt->setUInt16(1, itr->second.PacketInfo.Exp);
+ stmt->setUInt32(2, itr->second.PacketInfo.Health);
+ stmt->setUInt8(3, itr->second.PacketInfo.Quality);
+ stmt->setUInt16(4, itr->second.PacketInfo.Flags);
+ stmt->setString(5, itr->second.PacketInfo.Name);
+ stmt->setUInt32(6, _owner->GetBattlenetAccountId());
+ stmt->setUInt64(7, itr->first);
+ trans->Append(stmt);
+ itr->second.SaveInfo = BATTLE_PET_UNCHANGED;
+ ++itr;
+ break;
+ case BATTLE_PET_REMOVED:
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BATTLE_PETS);
+ stmt->setUInt32(0, _owner->GetBattlenetAccountId());
+ stmt->setUInt64(1, itr->first);
+ trans->Append(stmt);
+ itr = _pets.erase(itr);
+ break;
+ default:
+ ++itr;
+ break;
+ }
+ }
+
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BATTLE_PET_SLOTS);
+ stmt->setUInt32(0, _owner->GetBattlenetAccountId());
+ trans->Append(stmt);
+
+ for (WorldPackets::BattlePet::BattlePetSlot const& slot : _slots)
+ {
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BATTLE_PET_SLOTS);
+ stmt->setUInt8(0, slot.Index);
+ stmt->setUInt32(1, _owner->GetBattlenetAccountId());
+ stmt->setUInt64(2, slot.Pet.Guid.GetCounter());
+ stmt->setBool(3, slot.Locked);
+ trans->Append(stmt);
+ }
+}
+
+BattlePetMgr::BattlePet* BattlePetMgr::GetPet(ObjectGuid guid)
+{
+ auto itr = _pets.find(guid.GetCounter());
+ if (itr != _pets.end())
+ return &itr->second;
+
+ return nullptr;
+}
+
+void BattlePetMgr::AddPet(uint32 species, uint32 creatureId, uint16 breed, uint8 quality, uint16 level /*= 1*/)
+{
+ BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(species);
+ if (!battlePetSpecies) // should never happen
+ return;
+
+ BattlePet pet;
+ pet.PacketInfo.Guid = ObjectGuid::Create<HighGuid::BattlePet>(sObjectMgr->GetGenerator<HighGuid::BattlePet>().Generate());
+ pet.PacketInfo.Species = species;
+ pet.PacketInfo.CreatureID = creatureId;
+ pet.PacketInfo.Level = level;
+ pet.PacketInfo.Exp = 0;
+ pet.PacketInfo.Flags = 0;
+ pet.PacketInfo.Breed = breed;
+ pet.PacketInfo.Quality = quality;
+ pet.PacketInfo.Name = "";
+ pet.CalculateStats();
+ pet.PacketInfo.Health = pet.PacketInfo.MaxHealth;
+ pet.SaveInfo = BATTLE_PET_NEW;
+
+ _pets[pet.PacketInfo.Guid.GetCounter()] = pet;
+
+ std::vector<BattlePet> updates;
+ updates.push_back(pet);
+ SendUpdates(updates, true);
+
+ _owner->GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET, species);
+}
+
+void BattlePetMgr::RemovePet(ObjectGuid guid)
+{
+ BattlePet* pet = GetPet(guid);
+ if (!pet)
+ return;
+
+ pet->SaveInfo = BATTLE_PET_REMOVED;
+
+ // spell is not unlearned on retail
+ /*if (GetPetCount(pet->PacketInfo.Species) == 0)
+ if (BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species))
+ _owner->GetPlayer()->RemoveSpell(speciesEntry->SummonSpellID);*/
+}
+
+uint8 BattlePetMgr::GetPetCount(uint32 species) const
+{
+ uint8 count = 0;
+ for (auto& itr : _pets)
+ if (itr.second.PacketInfo.Species == species && itr.second.SaveInfo != BATTLE_PET_REMOVED)
+ count++;
+
+ return count;
+}
+
+void BattlePetMgr::UnlockSlot(uint8 slot)
+{
+ if (!_slots[slot].Locked)
+ return;
+
+ _slots[slot].Locked = false;
+
+ WorldPackets::BattlePet::PetBattleSlotUpdates updates;
+ updates.Slots.push_back(_slots[slot]);
+ updates.AutoSlotted = false; // what's this?
+ updates.NewSlot = true; // causes the "new slot unlocked" bubble to appear
+ _owner->SendPacket(updates.Write());
+}
+
+std::vector<BattlePetMgr::BattlePet> BattlePetMgr::GetLearnedPets() const
+{
+ std::vector<BattlePet> pets;
+ for (auto& pet : _pets)
+ if (pet.second.SaveInfo != BATTLE_PET_REMOVED)
+ pets.push_back(pet.second);
+
+ return pets;
+}
+
+void BattlePetMgr::CageBattlePet(ObjectGuid guid)
+{
+ BattlePet* pet = GetPet(guid);
+ if (!pet)
+ return;
+
+ ItemPosCountVec dest;
+
+ if (_owner->GetPlayer()->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, BATTLE_PET_CAGE_ITEM_ID, 1) != EQUIP_ERR_OK)
+ return;
+
+ Item* item = _owner->GetPlayer()->StoreNewItem(dest, BATTLE_PET_CAGE_ITEM_ID, true);
+ if (!item)
+ return;
+
+ item->SetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID, pet->PacketInfo.Species);
+ item->SetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA, pet->PacketInfo.Breed | (pet->PacketInfo.Quality << 24));
+ item->SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, pet->PacketInfo.Level);
+ item->SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, pet->PacketInfo.CreatureID);
+
+ // FIXME: "You create: ." - item name missing in chat
+ _owner->GetPlayer()->SendNewItem(item, 1, true, true);
+
+ RemovePet(guid);
+
+ WorldPackets::BattlePet::BattlePetDeleted deletePet;
+ deletePet.PetGuid = guid;
+ _owner->SendPacket(deletePet.Write());
+}
+
+void BattlePetMgr::HealBattlePetsPct(uint8 pct)
+{
+ // TODO: After each Pet Battle, any injured companion will automatically
+ // regain 50 % of the damage that was taken during combat
+ std::vector<BattlePet> updates;
+
+ for (auto& pet : _pets)
+ if (pet.second.PacketInfo.Health != pet.second.PacketInfo.MaxHealth)
+ {
+ pet.second.PacketInfo.Health += CalculatePct(pet.second.PacketInfo.MaxHealth, pct);
+ // don't allow Health to be greater than MaxHealth
+ pet.second.PacketInfo.Health = std::min(pet.second.PacketInfo.Health, pet.second.PacketInfo.MaxHealth);
+ if (pet.second.SaveInfo != BATTLE_PET_NEW)
+ pet.second.SaveInfo = BATTLE_PET_CHANGED;
+ updates.push_back(pet.second);
+ }
+
+ SendUpdates(updates, false);
+}
+
+void BattlePetMgr::SummonPet(ObjectGuid guid)
+{
+ BattlePet* pet = GetPet(guid);
+ if (!pet)
+ return;
+
+ BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species);
+ if (!speciesEntry)
+ return;
+
+ // TODO: set proper CreatureID for spell DEFAULT_SUMMON_BATTLE_PET_SPELL (default EffectMiscValueA is 40721 - Murkimus the Gladiator)
+ _owner->GetPlayer()->CastSpell(_owner->GetPlayer(), speciesEntry->SummonSpellID ? speciesEntry->SummonSpellID : DEFAULT_SUMMON_BATTLE_PET_SPELL);
+
+ // TODO: set pet level, quality... update fields
+}
+
+void BattlePetMgr::SendUpdates(std::vector<BattlePet> pets, bool petAdded)
+{
+ WorldPackets::BattlePet::BattlePetUpdates updates;
+ for (auto pet : pets)
+ updates.Pets.push_back(pet.PacketInfo);
+
+ updates.PetAdded = petAdded;
+ _owner->SendPacket(updates.Write());
+}
+
+void BattlePetMgr::SendError(BattlePetError error, uint32 creatureId)
+{
+ WorldPackets::BattlePet::BattlePetError battlePetError;
+ battlePetError.Result = error;
+ battlePetError.CreatureID = creatureId;
+ _owner->SendPacket(battlePetError.Write());
+}
diff --git a/src/server/game/BattlePets/BattlePetMgr.h b/src/server/game/BattlePets/BattlePetMgr.h
new file mode 100644
index 00000000000..97b1b34c13c
--- /dev/null
+++ b/src/server/game/BattlePets/BattlePetMgr.h
@@ -0,0 +1,141 @@
+/*
+ * 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 BattlePetMgr_h__
+#define BattlePetMgr_h__
+
+#include "DB2Stores.h"
+#include "BattlePetPackets.h"
+
+enum BattlePetMisc
+{
+ MAX_PET_BATTLE_SLOTS = 3,
+ MAX_BATTLE_PETS_PER_SPECIES = 3,
+ BATTLE_PET_CAGE_ITEM_ID = 82800,
+ DEFAULT_SUMMON_BATTLE_PET_SPELL = 118301
+};
+
+// TODO: fix values in this enum
+enum BattlePetError
+{
+ BATTLEPETRESULT_CANT_HAVE_MORE_PETS_OF_THAT_TYPE = 9,
+ BATTLEPETRESULT_TOO_HIGH_LEVEL_TO_UNCAGE = 12,
+ BATTLEPETRESULT_CANT_HAVE_MORE_PETS = 13,
+
+ // wrong order
+ BATTLEPETRESULT_DUPLICATE_CONVERTED_PET,
+ BATTLEPETRESULT_NEED_TO_UNLOCK,
+ BATTLEPETRESULT_BAD_PARAM,
+ BATTLEPETRESULT_LOCKED_PET_ALREADY_EXISTS,
+ BATTLEPETRESULT_OK,
+ BATTLEPETRESULT_UNCAPTURABLE,
+ BATTLEPETRESULT_CANT_INVALID_CHARACTER_GUID
+};
+
+// taken from BattlePetState.db2 - it seems to store some initial values for battle pets
+// there are only values used in BattlePetSpeciesState.db2 and BattlePetBreedState.db2
+// TODO: expand this enum if needed
+enum BattlePetState
+{
+ STATE_MAX_HEALTH_BONUS = 2,
+ STATE_INTERNAL_INITIAL_LEVEL = 17,
+ STATE_STAT_POWER = 18,
+ STATE_STAT_STAMINA = 19,
+ STATE_STAT_SPEED = 20,
+ STATE_MOD_DAMAGE_DEALT_PERCENT = 23,
+ STATE_GENDER = 78, // 1 - male, 2 - female
+ STATE_COSMETIC_WATER_BUBBLED = 85,
+ STATE_SPECIAL_IS_COCKROACH = 93,
+ STATE_COSMETIC_FLY_TIER = 128,
+ STATE_COSMETIC_BIGGLESWORTH = 144,
+ STATE_PASSIVE_ELITE = 153,
+ STATE_PASSIVE_BOSS = 162,
+ STATE_COSMETIC_TREASURE_GOBLIN = 176,
+ // these are not in BattlePetState.db2 but are used in BattlePetSpeciesState.db2
+ STATE_START_WITH_BUFF = 183,
+ STATE_START_WITH_BUFF_2 = 184
+};
+
+enum BattlePetSaveInfo
+{
+ BATTLE_PET_UNCHANGED = 0,
+ BATTLE_PET_CHANGED = 1,
+ BATTLE_PET_NEW = 2,
+ BATTLE_PET_REMOVED = 3
+};
+
+class BattlePetMgr
+{
+public:
+ struct BattlePet
+ {
+ void CalculateStats();
+
+ WorldPackets::BattlePet::BattlePet PacketInfo;
+ BattlePetSaveInfo SaveInfo;
+ };
+
+ explicit BattlePetMgr(WorldSession* owner);
+
+ static void Initialize();
+
+ static uint16 RollPetBreed(uint32 species);
+ static uint8 GetDefaultPetQuality(uint32 species);
+
+ void LoadFromDB(PreparedQueryResult pets, PreparedQueryResult slots);
+ void SaveToDB(SQLTransaction& trans);
+
+ BattlePet* GetPet(ObjectGuid guid);
+ void AddPet(uint32 species, uint32 creatureId, uint16 breed, uint8 quality, uint16 level = 1);
+ void RemovePet(ObjectGuid guid);
+
+ uint8 GetPetCount(uint32 species) const;
+
+ WorldPackets::BattlePet::BattlePetSlot* GetSlot(uint8 slot) { return &_slots[slot]; }
+ void UnlockSlot(uint8 slot);
+
+ WorldSession* GetOwner() const { return _owner; }
+
+ uint16 GetTrapLevel() const { return _trapLevel; }
+ std::vector<BattlePet> GetLearnedPets() const;
+ std::vector<WorldPackets::BattlePet::BattlePetSlot> GetSlots() const { return _slots; }
+
+ void CageBattlePet(ObjectGuid guid);
+ void HealBattlePetsPct(uint8 pct);
+
+ void SummonPet(ObjectGuid guid);
+
+ void SendUpdates(std::vector<BattlePet> pets, bool petAdded);
+ void SendError(BattlePetError error, uint32 creatureId);
+
+private:
+ WorldSession* _owner;
+ uint16 _trapLevel = 0;
+ std::unordered_map<uint64 /*battlePetGuid*/, BattlePet> _pets;
+ std::vector<WorldPackets::BattlePet::BattlePetSlot> _slots;
+
+ static void LoadAvailablePetBreeds();
+ static void LoadDefaultPetQualities();
+
+ // hash no longer required in C++14
+ static std::unordered_map<uint16 /*BreedID*/, std::unordered_map<BattlePetState /*state*/, int32 /*value*/, std::hash<std::underlying_type<BattlePetState>::type> >> _battlePetBreedStates;
+ static std::unordered_map<uint32 /*SpeciesID*/, std::unordered_map<BattlePetState /*state*/, int32 /*value*/, std::hash<std::underlying_type<BattlePetState>::type> >> _battlePetSpeciesStates;
+ static std::unordered_map<uint32 /*SpeciesID*/, std::unordered_set<uint8 /*breed*/>> _availableBreedsPerSpecies;
+ static std::unordered_map<uint32 /*SpeciesID*/, uint8 /*quality*/> _defaultQualityPerSpecies;
+};
+
+#endif // BattlePetMgr_h__
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index 0ed6b78d580..14bb8f25389 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -16,6 +16,7 @@ file(GLOB_RECURSE sources_AuctionHouse AuctionHouse/*.cpp AuctionHouse/*.h)
file(GLOB_RECURSE sources_AuctionHouseBot AuctionHouseBot/*.cpp AuctionHouseBot/*.h)
file(GLOB_RECURSE sources_Battlefield Battlefield/*.cpp Battlefield/*.h)
file(GLOB_RECURSE sources_Battlegrounds Battlegrounds/*.cpp Battlegrounds/*.h)
+file(GLOB_RECURSE sources_BattlePets BattlePets/*.cpp BattlePets/*.h)
file(GLOB_RECURSE sources_Calendar Calendar/*.cpp Calendar/*.h)
file(GLOB_RECURSE sources_Chat Chat/*.cpp Chat/*.h)
file(GLOB_RECURSE sources_Combat Combat/*.cpp Combat/*.h)
@@ -68,6 +69,7 @@ set(game_STAT_SRCS
${sources_AuctionHouseBot}
${sources_Battlefield}
${sources_Battlegrounds}
+ ${sources_BattlePets}
${sources_Calendar}
${sources_Chat}
${sources_Combat}
@@ -118,6 +120,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/Battlefield/Zones
${CMAKE_CURRENT_SOURCE_DIR}/Battlegrounds
${CMAKE_CURRENT_SOURCE_DIR}/Battlegrounds/Zones
+ ${CMAKE_CURRENT_SOURCE_DIR}/BattlePets
${CMAKE_CURRENT_SOURCE_DIR}/Calendar
${CMAKE_CURRENT_SOURCE_DIR}/Chat
${CMAKE_CURRENT_SOURCE_DIR}/Chat/Channels
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 0485715f287..ae26119986c 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -29,6 +29,10 @@ DB2Storage<AreaGroupEntry> sAreaGroupStore("AreaGroup.db2",
DB2Storage<AreaGroupMemberEntry> sAreaGroupMemberStore("AreaGroupMember.db2", AreaGroupMemberFormat, HOTFIX_SEL_AREA_GROUP_MEMBER);
DB2Storage<AuctionHouseEntry> sAuctionHouseStore("AuctionHouse.db2", AuctionHouseFormat, HOTFIX_SEL_AUCTION_HOUSE);
DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore("BarberShopStyle.db2", BarberShopStyleFormat, HOTFIX_SEL_BARBER_SHOP_STYLE);
+DB2Storage<BattlePetBreedQualityEntry> sBattlePetBreedQualityStore("BattlePetBreedQuality.db2", BattlePetBreedQualityFormat, HOTFIX_SEL_BATTLE_PET_BREED_QUALITY);
+DB2Storage<BattlePetBreedStateEntry> sBattlePetBreedStateStore("BattlePetBreedState.db2", BattlePetBreedStateFormat, HOTFIX_SEL_BATTLE_PET_BREED_STATE);
+DB2Storage<BattlePetSpeciesEntry> sBattlePetSpeciesStore("BattlePetSpecies.db2", BattlePetSpeciesFormat, HOTFIX_SEL_BATTLE_PET_SPECIES);
+DB2Storage<BattlePetSpeciesStateEntry> sBattlePetSpeciesStateStore("BattlePetSpeciesState.db2", BattlePetSpeciesStateFormat, HOTFIX_SEL_BATTLE_PET_SPECIES_STATE);
DB2Storage<BroadcastTextEntry> sBroadcastTextStore("BroadcastText.db2", BroadcastTextFormat, HOTFIX_SEL_BROADCAST_TEXT);
DB2Storage<CharStartOutfitEntry> sCharStartOutfitStore("CharStartOutfit.db2", CharStartOutfitFormat, HOTFIX_SEL_CHAR_START_OUTFIT);
DB2Storage<ChrClassesXPowerTypesEntry> sChrClassesXPowerTypesStore("ChrClassesXPowerTypes.db2", ChrClassesXPowerTypesFormat, HOTFIX_SEL_CHR_CLASSES_X_POWER_TYPES);
@@ -78,6 +82,7 @@ DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore("ItemRand
DB2Storage<ItemSparseEntry> sItemSparseStore("Item-sparse.db2", ItemSparseFormat, HOTFIX_SEL_ITEM_SPARSE);
DB2Storage<ItemSpecEntry> sItemSpecStore("ItemSpec.db2", ItemSpecFormat, HOTFIX_SEL_ITEM_SPEC);
DB2Storage<ItemSpecOverrideEntry> sItemSpecOverrideStore("ItemSpecOverride.db2", ItemSpecOverrideFormat, HOTFIX_SEL_ITEM_SPEC_OVERRIDE);
+DB2Storage<ItemToBattlePetSpeciesEntry> sItemToBattlePetSpeciesStore("ItemToBattlePetSpecies.db2", ItemToBattlePetSpeciesFormat, HOTFIX_SEL_ITEM_TO_BATTLE_PET_SPECIES);
DB2Storage<ItemXBonusTreeEntry> sItemXBonusTreeStore("ItemXBonusTree.db2", ItemXBonusTreeFormat, HOTFIX_SEL_ITEM_X_BONUS_TREE);
DB2Storage<KeyChainEntry> sKeyChainStore("KeyChain.db2", KeyChainFormat, HOTFIX_SEL_KEY_CHAIN);
DB2Storage<MailTemplateEntry> sMailTemplateStore("MailTemplate.db2", MailTemplateFormat, HOTFIX_SEL_MAIL_TEMPLATE);
@@ -196,8 +201,12 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sAchievementStore);
LOAD_DB2(sAreaGroupMemberStore);
LOAD_DB2(sAreaGroupStore);
+ LOAD_DB2(sBattlePetBreedQualityStore);
+ LOAD_DB2(sBattlePetBreedStateStore);
LOAD_DB2(sAuctionHouseStore);
LOAD_DB2(sBarberShopStyleStore);
+ LOAD_DB2(sBattlePetSpeciesStore);
+ LOAD_DB2(sBattlePetSpeciesStateStore);
LOAD_DB2(sBroadcastTextStore);
LOAD_DB2(sCharStartOutfitStore);
LOAD_DB2(sChrClassesXPowerTypesStore);
@@ -247,6 +256,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sItemSpecOverrideStore);
LOAD_DB2(sItemSpecStore);
LOAD_DB2(sItemStore);
+ LOAD_DB2(sItemToBattlePetSpeciesStore);
LOAD_DB2(sItemXBonusTreeStore);
LOAD_DB2(sKeyChainStore);
LOAD_DB2(sMailTemplateStore);
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index 685d251f685..4184ab5b5d4 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -27,6 +27,10 @@
extern DB2Storage<AchievementEntry> sAchievementStore;
extern DB2Storage<AuctionHouseEntry> sAuctionHouseStore;
extern DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore;
+extern DB2Storage<BattlePetBreedQualityEntry> sBattlePetBreedQualityStore;
+extern DB2Storage<BattlePetBreedStateEntry> sBattlePetBreedStateStore;
+extern DB2Storage<BattlePetSpeciesEntry> sBattlePetSpeciesStore;
+extern DB2Storage<BattlePetSpeciesStateEntry> sBattlePetSpeciesStateStore;
extern DB2Storage<BroadcastTextEntry> sBroadcastTextStore;
extern DB2Storage<CharStartOutfitEntry> sCharStartOutfitStore;
extern DB2Storage<CinematicSequencesEntry> sCinematicSequencesStore;
@@ -70,6 +74,7 @@ extern DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore;
extern DB2Storage<ItemSparseEntry> sItemSparseStore;
extern DB2Storage<ItemSpecEntry> sItemSpecStore;
extern DB2Storage<ItemSpecOverrideEntry> sItemSpecOverrideStore;
+extern DB2Storage<ItemToBattlePetSpeciesEntry> sItemToBattlePetSpeciesStore;
extern DB2Storage<MailTemplateEntry> sMailTemplateStore;
extern DB2Storage<ModifierTreeEntry> sModifierTreeStore;
extern DB2Storage<MountCapabilityEntry> sMountCapabilityStore;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 289d895c024..f2f8bec2b68 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -77,6 +77,42 @@ struct BarberShopStyleEntry
uint32 Data; // 7 (real ID to hair/facial hair)
};
+struct BattlePetBreedQualityEntry
+{
+ uint32 ID; // 0
+ uint32 Quality; // 1
+ float Modifier; // 2
+};
+
+struct BattlePetBreedStateEntry
+{
+ uint32 ID; // 0
+ uint32 BreedID; // 1
+ uint32 State; // 2
+ int32 Value; // 3
+};
+
+struct BattlePetSpeciesEntry
+{
+ uint32 ID; // 0
+ uint32 CreatureID; // 1
+ uint32 IconFileID; // 2
+ uint32 SummonSpellID; // 3
+ uint32 PetType; // 4
+ int32 Source; // 5
+ uint32 Flags; // 6
+ LocalizedString* SourceText; // 7
+ LocalizedString* Description; // 8
+};
+
+struct BattlePetSpeciesStateEntry
+{
+ uint32 ID; // 0
+ uint32 SpeciesID; // 1
+ uint32 State; // 2
+ int32 Value; // 3
+};
+
#define MAX_BROADCAST_TEXT_EMOTES 3
struct BroadcastTextEntry
@@ -830,6 +866,12 @@ struct ItemSpecOverrideEntry
uint32 SpecID; // 2
};
+struct ItemToBattlePetSpeciesEntry
+{
+ uint32 ID; // 0
+ uint32 BattlePetSpeciesID; // 1
+};
+
struct ItemXBonusTreeEntry
{
uint32 ID; // 0
diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h
index 0ebf8d2063e..ab9e2e0ae4e 100644
--- a/src/server/game/DataStores/DB2fmt.h
+++ b/src/server/game/DataStores/DB2fmt.h
@@ -23,6 +23,10 @@ char const AreaGroupFormat[] = "n";
char const AreaGroupMemberFormat[] = "nii";
char const AuctionHouseFormat[] = "niiis";
char const BarberShopStyleFormat[] = "nissfiii";
+char const BattlePetBreedQualityFormat[] = "nif";
+char const BattlePetBreedStateFormat[] = "niii";
+char const BattlePetSpeciesFormat[] = "niiiiiiss";
+char const BattlePetSpeciesStateFormat[] = "niii";
char const BroadcastTextFormat[] = "nissiiiiiiiii";
char const CharStartOutfitFormat[] = "nbbbbiiiiiiiiiiiiiiiiiiiiiiiiii";
char const ChrClassesXPowerTypesFormat[] = "iii";
@@ -72,6 +76,7 @@ char const ItemRandomSuffixFormat[] = "nssiiiiiiiiii";
char const ItemSparseFormat[] = "niiiiffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffffffiiifisssssiiiiiiiiiiiiiiiiiiifiiifiii";
char const ItemSpecFormat[] = "niiiiii";
char const ItemSpecOverrideFormat[] = "nii";
+char const ItemToBattlePetSpeciesFormat[] = "ni";
char const ItemXBonusTreeFormat[] = "nii";
char const KeyChainFormat[] = "nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char const MailTemplateFormat[] = "ns";
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index d7125293671..ed1a1470e91 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -159,6 +159,7 @@ enum AchievementCriteriaAdditionalCondition
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_RATED_BATTLEGROUND = 63, // NYI
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_PROJECT_RARITY = 65,
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_PROJECT_RACE = 66,
+ ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_BATTLE_PET_SPECIES = 91,
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_FOLLOWER_QUALITY = 145,
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_FOLLOWER_LEVEL = 146,
ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_RARE_MISSION = 147, // NYI
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 4d65598c298..6c7d390c069 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -351,6 +351,10 @@ void Item::SaveToDB(SQLTransaction& trans)
stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID) | (GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD) << 24));
stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_UPGRADE_ID));
stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION));
+ stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID));
+ stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA));
+ stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL));
+ stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID));
std::ostringstream bonusListIDs;
for (uint32 bonusListID : GetDynamicValues(ITEM_DYNAMIC_FIELD_BONUSLIST_IDS))
@@ -405,8 +409,10 @@ void Item::SaveToDB(SQLTransaction& trans)
bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fields, uint32 entry)
{
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
- //result = CharacterDatabase.PQuery("SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion, bonusListIDs FROM item_instance WHERE guid = '%u'", guid);
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12
+ //result = CharacterDatabase.PQuery("SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text,
+ // 13 14 15 16 17 18 19 20
+ // transmogrification, upgradeId, enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs FROM item_instance WHERE guid = '%u'", guid);
// create item before any checks for store correct guid
// and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB
@@ -482,8 +488,12 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie
}
SetModifier(ITEM_MODIFIER_UPGRADE_ID, fields[14].GetUInt32());
SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION, fields[15].GetUInt32());
+ SetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID, fields[16].GetUInt32());
+ SetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA, fields[17].GetUInt32());
+ SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, fields[18].GetUInt16());
+ SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, fields[19].GetUInt32());
- Tokenizer bonusListIDs(fields[16].GetString(), ' ');
+ Tokenizer bonusListIDs(fields[20].GetString(), ' ');
for (char const* token : bonusListIDs)
{
uint32 bonusListID = atoul(token);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 3b62f9f59a1..901bd81ecba 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -27,6 +27,7 @@
#include "Battleground.h"
#include "BattlegroundMgr.h"
#include "BattlegroundScore.h"
+#include "BattlePetMgr.h"
#include "CellImpl.h"
#include "ChannelMgr.h"
#include "CharacterDatabaseCleaner.h"
@@ -13699,10 +13700,10 @@ void Player::SendNewItem(Item* item, uint32 quantity, bool pushed, bool created,
packet.Quantity = quantity;
packet.QuantityInInventory = GetItemCount(item->GetEntry());
//packet.DungeonEncounterID;
- //packet.BattlePetBreedID;
- //packet.BattlePetBreedQuality;
- //packet.BattlePetSpeciesID;
- //packet.BattlePetLevel;
+ packet.BattlePetBreedID = item->GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA) & 0xFFFFFF;
+ packet.BattlePetBreedQuality = (item->GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA) >> 24) & 0xFF;
+ packet.BattlePetSpeciesID = item->GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID);
+ packet.BattlePetLevel = item->GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL);
packet.ItemGUID = item->GetGUID();
@@ -17650,8 +17651,10 @@ void Player::LoadCorpse()
void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
{
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
- // SELECT ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId, ii.enchantIllusion, ii.bonusListIDs, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ // SELECT ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId
+ // 15 16 17 18 19 20 21 22
+ // ii.enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, ii.bonusListIDs, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot
//NOTE: the "order by `bag`" is important because it makes sure
//the bagMap is filled before items in the bags are loaded
//NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
@@ -17673,8 +17676,8 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
Field* fields = result->Fetch();
if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields))
{
- ObjectGuid bagGuid = fields[17].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[17].GetUInt64()) : ObjectGuid::Empty;
- uint8 slot = fields[18].GetUInt8();
+ ObjectGuid bagGuid = fields[21].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[21].GetUInt64()) : ObjectGuid::Empty;
+ uint8 slot = fields[22].GetUInt8();
uint8 err = EQUIP_ERR_OK;
// Item is not in bag
@@ -17995,7 +17998,7 @@ void Player::_LoadMailedItems(Mail* mail)
Item* item = NewItemOrBag(proto);
- ObjectGuid ownerGuid = fields[17].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[17].GetUInt64()) : ObjectGuid::Empty;
+ ObjectGuid ownerGuid = fields[21].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[21].GetUInt64()) : ObjectGuid::Empty;
if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry))
{
TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: " UI64FMTD ", deleted from mail", mail->messageID, itemGuid);
@@ -19188,9 +19191,11 @@ void Player::SaveToDB(bool create /*=false*/)
CharacterDatabase.CommitTransaction(trans);
- SQLTransaction transLogin = LoginDatabase.BeginTransaction();
- GetSession()->SaveAccountToys(transLogin);
- LoginDatabase.CommitTransaction(transLogin);
+ // TODO: Move this out
+ trans = LoginDatabase.BeginTransaction();
+ GetSession()->SaveAccountToys(trans);
+ GetSession()->GetBattlePetMgr()->SaveToDB(trans);
+ LoginDatabase.CommitTransaction(trans);
// save pet (hunter pet level and experience and all type pets health/mana).
if (Pet* pet = GetPet())
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index cddcfd1ef80..d4973e0f332 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -532,7 +532,7 @@ enum PlayerFlags
PLAYER_FLAGS_UNK21 = 0x00200000,
PLAYER_FLAGS_COMMENTATOR2 = 0x00400000,
PLAYER_ALLOW_ONLY_ABILITY = 0x00800000, // used by bladestorm and killing spree, allowed only spells with SPELL_ATTR0_REQ_AMMO, SPELL_EFFECT_ATTACK, checked only for active player
- PLAYER_FLAGS_UNK24 = 0x01000000, // disabled all melee ability on tab include autoattack
+ PLAYER_FLAGS_PET_BATTLES_UNLOCKED = 0x01000000, // enables pet battles
PLAYER_FLAGS_NO_XP_GAIN = 0x02000000,
PLAYER_FLAGS_UNK26 = 0x04000000,
PLAYER_FLAGS_AUTO_DECLINE_GUILD = 0x08000000, // Automatically declines guild invites
diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp
index e93a1da83b3..e7347a00732 100644
--- a/src/server/game/Guilds/Guild.cpp
+++ b/src/server/game/Guilds/Guild.cpp
@@ -368,7 +368,7 @@ void Guild::BankTab::LoadFromDB(Field* fields)
bool Guild::BankTab::LoadItemFromDB(Field* fields)
{
- uint8 slotId = fields[19].GetUInt8();
+ uint8 slotId = fields[23].GetUInt8();
ObjectGuid::LowType itemGuid = fields[0].GetUInt64();
uint32 itemEntry = fields[1].GetUInt32();
if (slotId >= GUILD_BANK_MAX_SLOTS)
@@ -2395,7 +2395,7 @@ void Guild::LoadBankTabFromDB(Field* fields)
bool Guild::LoadBankItemFromDB(Field* fields)
{
- uint8 tabId = fields[18].GetUInt8();
+ uint8 tabId = fields[22].GetUInt8();
if (tabId >= _GetPurchasedTabsSize())
{
TC_LOG_ERROR("guild", "Invalid tab for item (GUID: %u, id: #%u) in guild bank, skipped.",
diff --git a/src/server/game/Guilds/GuildMgr.cpp b/src/server/game/Guilds/GuildMgr.cpp
index 44816f5fd67..6db5b84a696 100644
--- a/src/server/game/Guilds/GuildMgr.cpp
+++ b/src/server/game/Guilds/GuildMgr.cpp
@@ -395,10 +395,10 @@ void GuildMgr::LoadGuilds()
// Delete orphan guild bank items
CharacterDatabase.DirectExecute("DELETE gbi FROM guild_bank_item gbi LEFT JOIN guild g ON gbi.guildId = g.guildId WHERE g.guildId IS NULL");
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
- // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion, bonusListIDs,
- // 17 18 19
- // guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion,
+ // 17 18 19 20 21 22 23
+ // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid
PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_BANK_ITEMS));
if (!result)
@@ -411,7 +411,7 @@ void GuildMgr::LoadGuilds()
do
{
Field* fields = result->Fetch();
- uint64 guildId = fields[17].GetUInt64();
+ uint64 guildId = fields[21].GetUInt64();
if (Guild* guild = GetGuildById(guildId))
guild->LoadBankItemFromDB(fields);
diff --git a/src/server/game/Handlers/BattlePetHandler.cpp b/src/server/game/Handlers/BattlePetHandler.cpp
new file mode 100644
index 00000000000..bd44ab73791
--- /dev/null
+++ b/src/server/game/Handlers/BattlePetHandler.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "WorldSession.h"
+#include "BattlePetMgr.h"
+#include "BattlePetPackets.h"
+#include "Player.h"
+
+void WorldSession::HandleBattlePetRequestJournal(WorldPackets::BattlePet::BattlePetRequestJournal& /*battlePetRequestJournal*/)
+{
+ // TODO: Move this to BattlePetMgr::SendJournal() just to have all packets in one file
+ WorldPackets::BattlePet::BattlePetJournal battlePetJournal;
+ battlePetJournal.Trap = GetBattlePetMgr()->GetTrapLevel();
+
+ for (auto itr : GetBattlePetMgr()->GetLearnedPets())
+ battlePetJournal.Pets.push_back(itr.PacketInfo);
+
+ battlePetJournal.Slots = GetBattlePetMgr()->GetSlots();
+ SendPacket(battlePetJournal.Write());
+}
+
+void WorldSession::HandleBattlePetSetBattleSlot(WorldPackets::BattlePet::BattlePetSetBattleSlot& battlePetSetBattleSlot)
+{
+ if (BattlePetMgr::BattlePet* pet = GetBattlePetMgr()->GetPet(battlePetSetBattleSlot.PetGuid))
+ GetBattlePetMgr()->GetSlot(battlePetSetBattleSlot.Slot)->Pet = pet->PacketInfo;
+}
+
+void WorldSession::HandleBattlePetModifyName(WorldPackets::BattlePet::BattlePetModifyName& battlePetModifyName)
+{
+ if (BattlePetMgr::BattlePet* pet = GetBattlePetMgr()->GetPet(battlePetModifyName.PetGuid))
+ {
+ pet->PacketInfo.Name = battlePetModifyName.Name;
+
+ if (pet->SaveInfo != BATTLE_PET_NEW)
+ pet->SaveInfo = BATTLE_PET_CHANGED;
+ }
+}
+
+void WorldSession::HandleBattlePetDeletePet(WorldPackets::BattlePet::BattlePetDeletePet& battlePetDeletePet)
+{
+ GetBattlePetMgr()->RemovePet(battlePetDeletePet.PetGuid);
+}
+
+void WorldSession::HandleBattlePetSetFlags(WorldPackets::BattlePet::BattlePetSetFlags& battlePetSetFlags)
+{
+ if (BattlePetMgr::BattlePet* pet = GetBattlePetMgr()->GetPet(battlePetSetFlags.PetGuid))
+ {
+ if (battlePetSetFlags.ControlType == 2) // 2 - apply
+ pet->PacketInfo.Flags |= battlePetSetFlags.Flags;
+ else // 3 - remove
+ pet->PacketInfo.Flags &= ~battlePetSetFlags.Flags;
+
+ if (pet->SaveInfo != BATTLE_PET_NEW)
+ pet->SaveInfo = BATTLE_PET_CHANGED;
+ }
+}
+
+void WorldSession::HandleCageBattlePet(WorldPackets::BattlePet::CageBattlePet& cageBattlePet)
+{
+ GetBattlePetMgr()->CageBattlePet(cageBattlePet.PetGuid);
+}
+
+void WorldSession::HandleBattlePetSummon(WorldPackets::BattlePet::BattlePetSummon& battlePetSummon)
+{
+ GetBattlePetMgr()->SummonPet(battlePetSummon.PetGuid);
+}
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index dfbd48f0dec..d50e721fb1d 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -22,6 +22,7 @@
#include "AuthenticationPackets.h"
#include "Battleground.h"
#include "BattlenetServerManager.h"
+#include "BattlePetPackets.h"
#include "CalendarMgr.h"
#include "CharacterPackets.h"
#include "Chat.h"
@@ -985,6 +986,10 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
hotfixInfo.Hotfixes = sDB2Manager.GetHotfixData();
SendPacket(hotfixInfo.Write());
+ // TODO: Move this to BattlePetMgr::SendJournalLock() just to have all packets in one file
+ WorldPackets::BattlePet::BattlePetJournalLockAcquired lock;
+ SendPacket(lock.Write());
+
pCurrChar->SendInitialPacketsBeforeAddToMap();
//Show cinematic at the first time that player login
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 425f021ed22..ccd3dd77109 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -27,6 +27,7 @@
#include "DB2Stores.h"
#include "NPCPackets.h"
#include "ItemPackets.h"
+#include "BattlePetMgr.h"
void WorldSession::HandleSplitItemOpcode(WorldPackets::Item::SplitItem& splitItem)
{
@@ -1264,3 +1265,21 @@ bool WorldSession::CanUseBank(ObjectGuid bankerGUID) const
return true;
}
+
+void WorldSession::HandleUseCritterItem(WorldPackets::Item::UseCritterItem& useCritterItem)
+{
+ Item* item = _player->GetItemByGuid(useCritterItem.ItemGuid);
+ if (!item)
+ return;
+
+ ItemToBattlePetSpeciesEntry const* itemToBattlePetSpecies = sItemToBattlePetSpeciesStore.LookupEntry(item->GetEntry());
+ if (!itemToBattlePetSpecies)
+ return;
+
+ BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(itemToBattlePetSpecies->BattlePetSpeciesID);
+ if (!battlePetSpecies)
+ return;
+
+ GetBattlePetMgr()->AddPet(battlePetSpecies->ID, battlePetSpecies->CreatureID, BattlePetMgr::RollPetBreed(battlePetSpecies->ID), BattlePetMgr::GetDefaultPetQuality(battlePetSpecies->ID));
+ _player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
+}
diff --git a/src/server/game/Server/Packets/BattlePetPackets.cpp b/src/server/game/Server/Packets/BattlePetPackets.cpp
new file mode 100644
index 00000000000..435b9f54338
--- /dev/null
+++ b/src/server/game/Server/Packets/BattlePetPackets.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 "BattlePetPackets.h"
+#include "World.h"
+
+ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::BattlePet::BattlePetSlot const& slot)
+{
+ data << (slot.Pet.Guid.IsEmpty() ? ObjectGuid::Create<HighGuid::BattlePet>(0) : slot.Pet.Guid);
+ data << slot.CollarID;
+ data << slot.Index;
+ data.WriteBit(slot.Locked);
+ data.FlushBits();
+
+ return data;
+}
+
+ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::BattlePet::BattlePet const& pet)
+{
+ data << pet.Guid;
+ data << pet.Species;
+ data << pet.CreatureID;
+ data << pet.CollarID;
+ data << pet.Breed;
+ data << pet.Level;
+ data << pet.Exp;
+ data << pet.Flags;
+ data << pet.Power;
+ data << pet.Health;
+ data << pet.MaxHealth;
+ data << pet.Speed;
+ data << pet.Quality;
+ data.WriteBits(pet.Name.size(), 7);
+ data.WriteBit(!pet.Owner.IsEmpty()); // HasOwnerInfo
+ data.WriteBit(pet.Name.empty()); // NoRename
+ data.FlushBits();
+
+ if (!pet.Owner.IsEmpty())
+ {
+ data << pet.Owner;
+ data << GetVirtualRealmAddress(); // Virtual
+ data << GetVirtualRealmAddress(); // Native
+ }
+
+ data.WriteString(pet.Name);
+
+ return data;
+}
+
+WorldPacket const* WorldPackets::BattlePet::BattlePetJournal::Write()
+{
+ _worldPacket << Trap;
+ _worldPacket << Slots.size();
+ _worldPacket << Pets.size();
+
+ for (auto const& slot : Slots)
+ _worldPacket << slot;
+
+ for (auto const& pet : Pets)
+ _worldPacket << pet;
+
+ _worldPacket.WriteBit(1); // HasJournalLock
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::BattlePet::BattlePetUpdates::Write()
+{
+ _worldPacket << Pets.size();
+
+ for (auto const& pet : Pets)
+ _worldPacket << pet;
+
+ _worldPacket.WriteBit(PetAdded);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
+
+
+WorldPacket const* WorldPackets::BattlePet::PetBattleSlotUpdates::Write()
+{
+ _worldPacket << Slots.size();
+
+ for (auto const& slot : Slots)
+ _worldPacket << slot;
+
+ _worldPacket.WriteBit(NewSlot);
+ _worldPacket.WriteBit(AutoSlotted);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
+
+void WorldPackets::BattlePet::BattlePetSetBattleSlot::Read()
+{
+ _worldPacket >> PetGuid;
+ _worldPacket >> Slot;
+}
+
+void WorldPackets::BattlePet::BattlePetModifyName::Read()
+{
+ _worldPacket >> PetGuid;
+ uint32 nameLength = _worldPacket.ReadBits(7);
+ bool hasDeclinedNames = _worldPacket.ReadBit();
+ Name = _worldPacket.ReadString(nameLength);
+
+ if (hasDeclinedNames)
+ {
+ uint8 declinedNameLengths[MAX_DECLINED_NAME_CASES];
+
+ for (uint8 i = 0; i < 5; ++i)
+ declinedNameLengths[i] = _worldPacket.ReadBits(7);
+
+ for (uint8 i = 0; i < 5; ++i)
+ Declined.name[i] = _worldPacket.ReadString(declinedNameLengths[i]);
+ }
+}
+
+void WorldPackets::BattlePet::BattlePetDeletePet::Read()
+{
+ _worldPacket >> PetGuid;
+}
+
+void WorldPackets::BattlePet::BattlePetSetFlags::Read()
+{
+ _worldPacket >> PetGuid;
+ _worldPacket >> Flags;
+ ControlType = _worldPacket.ReadBits(2);
+}
+
+void WorldPackets::BattlePet::CageBattlePet::Read()
+{
+ _worldPacket >> PetGuid;
+}
+
+WorldPacket const* WorldPackets::BattlePet::BattlePetDeleted::Write()
+{
+ _worldPacket << PetGuid;
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::BattlePet::BattlePetError::Write()
+{
+ _worldPacket.WriteBits(Result, 4);
+ _worldPacket.FlushBits();
+ _worldPacket << CreatureID;
+
+ return &_worldPacket;
+}
+
+void WorldPackets::BattlePet::BattlePetSummon::Read()
+{
+ _worldPacket >> PetGuid;
+}
diff --git a/src/server/game/Server/Packets/BattlePetPackets.h b/src/server/game/Server/Packets/BattlePetPackets.h
new file mode 100644
index 00000000000..844cb855464
--- /dev/null
+++ b/src/server/game/Server/Packets/BattlePetPackets.h
@@ -0,0 +1,194 @@
+/*
+ * 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 BattlePetPackets_h__
+#define BattlePetPackets_h__
+
+#include "Packet.h"
+#include "ObjectGuid.h"
+
+namespace WorldPackets
+{
+ namespace BattlePet
+ {
+ struct BattlePet
+ {
+ ObjectGuid Guid;
+ uint32 Species = 0;
+ uint32 CreatureID = 0;
+ uint32 CollarID = 0; // what's this?
+ uint16 Breed = 0;
+ uint16 Level = 0;
+ uint16 Exp = 0;
+ uint16 Flags = 0;
+ uint32 Power = 0;
+ uint32 Health = 0;
+ uint32 MaxHealth = 0;
+ uint32 Speed = 0;
+ uint8 Quality = 0;
+ ObjectGuid Owner; // for non-account wide pets only? (Guild Page, Guild Herald)
+ std::string Name;
+ };
+
+ struct BattlePetSlot
+ {
+ BattlePet Pet;
+ uint32 CollarID = 0; // what's this?
+ uint8 Index = 0;
+ bool Locked = true;
+ };
+
+ class BattlePetJournal final : public ServerPacket
+ {
+ public:
+ BattlePetJournal() : ServerPacket(SMSG_BATTLE_PET_JOURNAL) { }
+
+ WorldPacket const* Write() override;
+
+ uint16 Trap = 0;
+ std::vector<BattlePetSlot> Slots;
+ std::vector<BattlePet> Pets;
+ };
+
+ class BattlePetJournalLockAcquired final : public ServerPacket
+ {
+ public:
+ BattlePetJournalLockAcquired() : ServerPacket(SMSG_BATTLE_PET_JOURNAL_LOCK_ACQUIRED, 0) { }
+
+ WorldPacket const* Write() override { return &_worldPacket; }
+ };
+
+ class BattlePetRequestJournal final : public ClientPacket
+ {
+ public:
+ BattlePetRequestJournal(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_REQUEST_JOURNAL, std::move(packet)) { }
+
+ void Read() override { }
+ };
+
+ class BattlePetUpdates final : public ServerPacket
+ {
+ public:
+ BattlePetUpdates() : ServerPacket(SMSG_BATTLE_PET_UPDATES) { }
+
+ WorldPacket const* Write() override;
+
+ std::vector<BattlePet> Pets;
+ bool PetAdded = false;
+ };
+
+ class PetBattleSlotUpdates final : public ServerPacket
+ {
+ public:
+ PetBattleSlotUpdates() : ServerPacket(SMSG_PET_BATTLE_SLOT_UPDATES) { }
+
+ WorldPacket const* Write() override;
+
+ std::vector<BattlePetSlot> Slots;
+ bool AutoSlotted = false;
+ bool NewSlot = false;
+ };
+
+ class BattlePetSetBattleSlot final : public ClientPacket
+ {
+ public:
+ BattlePetSetBattleSlot(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_SET_BATTLE_SLOT, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid PetGuid;
+ uint8 Slot = 0;
+ };
+
+ class BattlePetModifyName final : public ClientPacket
+ {
+ public:
+ BattlePetModifyName(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_MODIFY_NAME, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid PetGuid;
+ std::string Name;
+ DeclinedName Declined;
+ };
+
+ class BattlePetDeletePet final : public ClientPacket
+ {
+ public:
+ BattlePetDeletePet(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_DELETE_PET, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid PetGuid;
+ };
+
+ class BattlePetSetFlags final : public ClientPacket
+ {
+ public:
+ BattlePetSetFlags(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_SET_FLAGS, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid PetGuid;
+ uint32 Flags = 0;
+ uint8 ControlType = 0;
+ };
+
+ class CageBattlePet final : public ClientPacket
+ {
+ public:
+ CageBattlePet(WorldPacket&& packet) : ClientPacket(CMSG_CAGE_BATTLE_PET, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid PetGuid;
+ };
+
+ class BattlePetDeleted final : public ServerPacket
+ {
+ public:
+ BattlePetDeleted() : ServerPacket(SMSG_BATTLE_PET_DELETED, 16) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid PetGuid;
+ };
+
+ class BattlePetError final : public ServerPacket
+ {
+ public:
+ BattlePetError() : ServerPacket(SMSG_BATTLE_PET_ERROR, 5) { }
+
+ WorldPacket const* Write() override;
+
+ uint8 Result = 0;
+ uint32 CreatureID = 0;
+ };
+
+ class BattlePetSummon final : public ClientPacket
+ {
+ public:
+ BattlePetSummon(WorldPacket&& packet) : ClientPacket(CMSG_BATTLE_PET_SUMMON, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid PetGuid;
+ };
+ }
+}
+
+#endif // BattlePetPackets_h__
diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp
index 80bafbba104..b6b69b84eac 100644
--- a/src/server/game/Server/Packets/ItemPackets.cpp
+++ b/src/server/game/Server/Packets/ItemPackets.cpp
@@ -461,3 +461,8 @@ void WorldPackets::Item::TransmogrifyItems::Read()
for (TransmogrifyItem& item : Items)
_worldPacket >> item;
}
+
+void WorldPackets::Item::UseCritterItem::Read()
+{
+ _worldPacket >> ItemGuid;
+}
diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h
index 943a681edbe..db7e9a01c0a 100644
--- a/src/server/game/Server/Packets/ItemPackets.h
+++ b/src/server/game/Server/Packets/ItemPackets.h
@@ -428,6 +428,16 @@ namespace WorldPackets
Array<TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS> Items;
};
+ class UseCritterItem final : public ClientPacket
+ {
+ public:
+ UseCritterItem(WorldPacket&& packet) : ClientPacket(CMSG_USE_CRITTER_ITEM, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid ItemGuid;
+ };
+
ByteBuffer& operator>>(ByteBuffer& data, InvUpdate& invUpdate);
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 24a01eb9057..a214fd44556 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -22,6 +22,7 @@
#include "Packets/AuctionHousePackets.h"
#include "Packets/BankPackets.h"
#include "Packets/BattlegroundPackets.h"
+#include "Packets/BattlePetPackets.h"
#include "Packets/BlackMarketPackets.h"
#include "Packets/CalendarPackets.h"
#include "Packets/ChannelPackets.h"
@@ -205,14 +206,14 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_BATTLE_PAY_GET_PURCHASE_LIST, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BATTLE_PAY_START_PURCHASE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BATTLE_PAY_START_VAS_PURCHASE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BattlePet::BattlePetDeletePet, &WorldSession::HandleBattlePetDeletePet);
DEFINE_HANDLER(CMSG_BATTLE_PET_DELETE_PET_CHEAT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_BATTLE_PET_MODIFY_NAME, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_BATTLE_PET_MODIFY_NAME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BattlePet::BattlePetModifyName, &WorldSession::HandleBattlePetModifyName);
+ DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BattlePet::BattlePetRequestJournal, &WorldSession::HandleBattlePetRequestJournal);
DEFINE_HANDLER(CMSG_BATTLE_PET_REQUEST_JOURNAL_LOCK, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_BATTLE_PET_SET_BATTLE_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_BATTLE_PET_SET_FLAGS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_BATTLE_PET_SUMMON, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_BATTLE_PET_SET_BATTLE_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BattlePet::BattlePetSetBattleSlot, &WorldSession::HandleBattlePetSetBattleSlot);
+ DEFINE_HANDLER(CMSG_BATTLE_PET_SET_FLAGS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BattlePet::BattlePetSetFlags, &WorldSession::HandleBattlePetSetFlags);
+ DEFINE_HANDLER(CMSG_BATTLE_PET_SUMMON, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::BattlePet::BattlePetSummon, &WorldSession::HandleBattlePetSummon);
DEFINE_HANDLER(CMSG_BATTLE_PET_UPDATE_NOTIFY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BEGIN_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Trade::BeginTrade, &WorldSession::HandleBeginTradeOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_BF_MGR_ENTRY_INVITE_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::HandleBfEntryInviteResponse );
@@ -231,7 +232,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_BUY_REAGENT_BANK, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BUY_WOW_TOKEN_CONFIRM, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_BUY_WOW_TOKEN_START, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_CAGE_BATTLE_PET, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_CAGE_BATTLE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BattlePet::CageBattlePet, &WorldSession::HandleCageBattlePet);
DEFINE_HANDLER(CMSG_CALENDAR_ADD_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Calendar::CalendarAddEvent, &WorldSession::HandleCalendarAddEvent);
DEFINE_OPCODE_HANDLER_OLD(CMSG_CALENDAR_COMPLAIN, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarComplain );
DEFINE_HANDLER(CMSG_CALENDAR_COPY_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Calendar::CalendarCopyEvent, &WorldSession::HandleCalendarCopyEvent);
@@ -813,7 +814,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_UPGRADE_GARRISON, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_UPGRADE_ITEM, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_USED_FOLLOW, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_USE_CRITTER_ITEM, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_USE_CRITTER_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::UseCritterItem, &WorldSession::HandleUseCritterItem);
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_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Toy::UseToy, &WorldSession::HandleUseToy);
@@ -916,16 +917,16 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PAY_START_PURCHASE_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PETS_HEALED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_CAGE_DATE_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_DELETED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_JOURNAL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_JOURNAL_LOCK_ACQUIRED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_DELETED, STATUS_NEVER, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_ERROR, STATUS_NEVER, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_JOURNAL, STATUS_NEVER, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_JOURNAL_LOCK_ACQUIRED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_JOURNAL_LOCK_DENIED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_LICENSE_CHANGED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_RESTORED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_REVOKED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_TRAP_LEVEL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_UPDATES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_UPDATES, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BF_MGR_DROP_TIMER_CANCELLED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BF_MGR_DROP_TIMER_STARTED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_BF_MGR_EJECTED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
@@ -1458,7 +1459,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_BATTLE_REPLACEMENTS_MADE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_BATTLE_REQUEST_FAILED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_BATTLE_ROUND_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_BATTLE_SLOT_UPDATES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_BATTLE_SLOT_UPDATES, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_CAST_FAILED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_CLEAR_SPELLS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_PET_DISMISS_SOUND, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 37f2e2899fc..4e60c366a66 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -48,6 +48,7 @@
#include "ClientConfigPackets.h"
#include "MiscPackets.h"
#include "ChatPackets.h"
+#include "BattlePetMgr.h"
#include "PacketUtilities.h"
#include <zlib.h>
@@ -130,7 +131,8 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun
_RBACData(NULL),
expireTime(60000), // 1 min after socket loss, session is deleted
forceExit(false),
- m_currentBankerGUID()
+ m_currentBankerGUID(),
+ _battlePetMgr(Trinity::make_unique<BattlePetMgr>(this))
{
memset(_tutorials, 0, sizeof(_tutorials));
@@ -1191,6 +1193,8 @@ public:
enum
{
GLOBAL_ACCOUNT_TOYS = 0,
+ BATTLE_PETS,
+ BATTLE_PET_SLOTS,
MAX_QUERIES
};
@@ -1205,6 +1209,14 @@ public:
stmt->setUInt32(0, battlenetAccountId);
ok = SetPreparedQuery(GLOBAL_ACCOUNT_TOYS, stmt) && ok;
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BATTLE_PETS);
+ stmt->setUInt32(0, battlenetAccountId);
+ ok = SetPreparedQuery(BATTLE_PETS, stmt) && ok;
+
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BATTLE_PET_SLOTS);
+ stmt->setUInt32(0, battlenetAccountId);
+ ok = SetPreparedQuery(BATTLE_PET_SLOTS, stmt) && ok;
+
return ok;
}
};
@@ -1252,6 +1264,9 @@ void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQue
SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION));
SendTutorialsData();
+ _battlePetMgr->LoadFromDB(holder->GetPreparedResult(AccountInfoQueryHolder::BATTLE_PETS),
+ holder->GetPreparedResult(AccountInfoQueryHolder::BATTLE_PET_SLOTS));
+
delete realmHolder;
delete holder;
}
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index bc1faf778df..54c794ad0ca 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -33,6 +33,7 @@
#include "AccountMgr.h"
#include <unordered_set>
+class BattlePetMgr;
class Channel;
class Creature;
class GameObject;
@@ -117,6 +118,17 @@ namespace WorldPackets
class ReportPvPPlayerAFK;
}
+ namespace BattlePet
+ {
+ class BattlePetRequestJournal;
+ class BattlePetSetBattleSlot;
+ class BattlePetModifyName;
+ class BattlePetDeletePet;
+ class BattlePetSetFlags;
+ class BattlePetSummon;
+ class CageBattlePet;
+ }
+
namespace BlackMarket
{
class BlackMarketOpen;
@@ -320,6 +332,7 @@ namespace WorldPackets
class WrapItem;
class CancelTempEnchantment;
class TransmogrifyItems;
+ class UseCritterItem;
}
namespace Loot
@@ -987,6 +1000,9 @@ class WorldSession
uint32 GetRecruiterId() const { return recruiterId; }
bool IsARecruiter() const { return isRecruiter; }
+ // Battle Pets
+ BattlePetMgr* GetBattlePetMgr() const { return _battlePetMgr.get(); }
+
public: // opcodes handlers
void Handle_NULL(WorldPackets::Null& null); // not used
@@ -1306,6 +1322,7 @@ class WorldSession
void HandleSwapItem(WorldPackets::Item::SwapItem& swapItem);
void HandleBuybackItem(WorldPackets::Item::BuyBackItem& packet);
void HandleWrapItem(WorldPackets::Item::WrapItem& packet);
+ void HandleUseCritterItem(WorldPackets::Item::UseCritterItem& packet);
void HandleAttackSwingOpcode(WorldPackets::Combat::AttackSwing& packet);
void HandleAttackStopOpcode(WorldPackets::Combat::AttackStop& packet);
@@ -1581,6 +1598,15 @@ class WorldSession
void HandleGarrisonRequestBlueprintAndSpecializationData(WorldPackets::Garrison::GarrisonRequestBlueprintAndSpecializationData& garrisonRequestBlueprintAndSpecializationData);
void HandleGarrisonGetBuildingLandmarks(WorldPackets::Garrison::GarrisonGetBuildingLandmarks& garrisonGetBuildingLandmarks);
+ // Battle Pets
+ void HandleBattlePetRequestJournal(WorldPackets::BattlePet::BattlePetRequestJournal& battlePetRequestJournal);
+ void HandleBattlePetSetBattleSlot(WorldPackets::BattlePet::BattlePetSetBattleSlot& battlePetSetBattleSlot);
+ void HandleBattlePetModifyName(WorldPackets::BattlePet::BattlePetModifyName& battlePetModifyName);
+ void HandleBattlePetDeletePet(WorldPackets::BattlePet::BattlePetDeletePet& battlePetDeletePet);
+ void HandleBattlePetSetFlags(WorldPackets::BattlePet::BattlePetSetFlags& battlePetSetFlags);
+ void HandleBattlePetSummon(WorldPackets::BattlePet::BattlePetSummon& battlePetSummon);
+ void HandleCageBattlePet(WorldPackets::BattlePet::CageBattlePet& cageBattlePet);
+
private:
void InitializeQueryCallbackParameters();
void ProcessQueryCallbacks();
@@ -1694,6 +1720,8 @@ class WorldSession
ObjectGuid m_currentBankerGUID;
ToyBoxContainer _toys;
+ std::unique_ptr<BattlePetMgr> _battlePetMgr;
+
WorldSession(WorldSession const& right) = delete;
WorldSession& operator=(WorldSession const& right) = delete;
};
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index c894c53aaf2..d5ed0a3c0a8 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -434,6 +434,9 @@ class Spell
void EffectCreateGarrison(SpellEffIndex effIndex);
void EffectAddGarrisonFollower(SpellEffIndex effIndex);
void EffectActivateGarrisonBuilding(SpellEffIndex effIndex);
+ void EffectHealBattlePetPct(SpellEffIndex effIndex);
+ void EffectEnableBattlePets(SpellEffIndex effIndex);
+ void EffectUncageBattlePet(SpellEffIndex effIndex);
typedef std::set<Aura*> UsedSpellMods;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index d10f76b76d3..1cb344da5aa 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -56,6 +56,7 @@
#include "Guild.h"
#include "ReputationMgr.h"
#include "AreaTrigger.h"
+#include "BattlePetMgr.h"
#include "Garrison.h"
#include "CombatLogPackets.h"
#include "DuelPackets.h"
@@ -256,7 +257,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //189 SPELL_EFFECT_LOOT
&Spell::EffectNULL, //190 SPELL_EFFECT_190
&Spell::EffectNULL, //191 SPELL_EFFECT_TELEPORT_TO_DIGSITE
- &Spell::EffectNULL, //192 SPELL_EFFECT_UNCAGE_BATTLEPET
+ &Spell::EffectUncageBattlePet, //192 SPELL_EFFECT_UNCAGE_BATTLEPET
&Spell::EffectNULL, //193 SPELL_EFFECT_START_PET_BATTLE
&Spell::EffectNULL, //194 SPELL_EFFECT_194
&Spell::EffectNULL, //195 SPELL_EFFECT_195
@@ -264,8 +265,8 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //197 SPELL_EFFECT_197
&Spell::EffectNULL, //198 SPELL_EFFECT_198
&Spell::EffectNULL, //199 SPELL_EFFECT_199
- &Spell::EffectNULL, //200 SPELL_EFFECT_HEAL_BATTLEPET_PCT
- &Spell::EffectNULL, //201 SPELL_EFFECT_ENABLE_BATTLE_PETS
+ &Spell::EffectHealBattlePetPct, //200 SPELL_EFFECT_HEAL_BATTLEPET_PCT
+ &Spell::EffectEnableBattlePets, //201 SPELL_EFFECT_ENABLE_BATTLE_PETS
&Spell::EffectNULL, //202 SPELL_EFFECT_202
&Spell::EffectNULL, //203 SPELL_EFFECT_203
&Spell::EffectNULL, //204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY
@@ -2259,6 +2260,22 @@ void Spell::EffectLearnSpell(SpellEffIndex effIndex)
uint32 spellToLearn = (m_spellInfo->Id == 483 || m_spellInfo->Id == 55884) ? damage : effectInfo->TriggerSpell;
player->LearnSpell(spellToLearn, false);
+ if (m_spellInfo->Id == 55884)
+ {
+ if (BattlePetMgr* battlePetMgr = player->GetSession()->GetBattlePetMgr())
+ {
+ for (auto entry : sBattlePetSpeciesStore)
+ {
+ if (entry->SummonSpellID == spellToLearn)
+ {
+ battlePetMgr->AddPet(entry->ID, entry->CreatureID, BattlePetMgr::RollPetBreed(entry->ID), BattlePetMgr::GetDefaultPetQuality(entry->ID));
+ player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT);
+ break;
+ }
+ }
+ }
+ }
+
TC_LOG_DEBUG("spells", "Spell: %s has learned spell %u from %s", player->GetGUID().ToString().c_str(), spellToLearn, m_caster->GetGUID().ToString().c_str());
}
@@ -5861,3 +5878,85 @@ void Spell::EffectActivateGarrisonBuilding(SpellEffIndex effIndex)
if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
garrison->ActivateBuilding(GetEffect(effIndex)->MiscValue);
}
+
+void Spell::EffectHealBattlePetPct(SpellEffIndex effIndex)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (BattlePetMgr* battlePetMgr = unitTarget->ToPlayer()->GetSession()->GetBattlePetMgr())
+ battlePetMgr->HealBattlePetsPct(GetEffect(effIndex)->BasePoints);
+}
+
+void Spell::EffectEnableBattlePets(SpellEffIndex /*effIndex*/)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* plr = unitTarget->ToPlayer();
+ plr->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_PET_BATTLES_UNLOCKED);
+ plr->GetSession()->GetBattlePetMgr()->UnlockSlot(0);
+}
+
+void Spell::EffectUncageBattlePet(SpellEffIndex /*effIndex*/)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
+ return;
+
+ if (!m_CastItem || !m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* plr = m_caster->ToPlayer();
+
+ // are we allowed to learn battle pets without it?
+ /*if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_PET_BATTLES_UNLOCKED))
+ return; // send some error*/
+
+ uint32 speciesId = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID);
+ uint16 breed = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA) & 0xFFFFFF;
+ uint8 quality = (m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA) >> 24) & 0xFF;
+ uint16 level = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL);
+ uint32 creatureId = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID);
+
+ BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(speciesId);
+ if (!speciesEntry)
+ return;
+
+ BattlePetMgr* battlePetMgr = plr->GetSession()->GetBattlePetMgr();
+ if (!battlePetMgr)
+ return;
+
+ uint16 maxLearnedLevel = 0;
+
+ for (auto pet : battlePetMgr->GetLearnedPets())
+ maxLearnedLevel = std::max(pet.PacketInfo.Level, maxLearnedLevel);
+
+ // TODO: This means if you put your highest lvl pet into cage, you won't be able to uncage it again which is probably wrong.
+ // We will need to store maxLearnedLevel somewhere to avoid this behaviour.
+ if (maxLearnedLevel < level)
+ {
+ battlePetMgr->SendError(BATTLEPETRESULT_TOO_HIGH_LEVEL_TO_UNCAGE, creatureId); // or speciesEntry.CreatureID
+ SendCastResult(SPELL_FAILED_CANT_ADD_BATTLE_PET);
+ return;
+ }
+
+ if (battlePetMgr->GetPetCount(speciesId) >= MAX_BATTLE_PETS_PER_SPECIES)
+ {
+ battlePetMgr->SendError(BATTLEPETRESULT_CANT_HAVE_MORE_PETS_OF_THAT_TYPE, creatureId); // or speciesEntry.CreatureID
+ SendCastResult(SPELL_FAILED_CANT_ADD_BATTLE_PET);
+ return;
+ }
+
+ if (!plr->HasSpell(speciesEntry->SummonSpellID))
+ plr->LearnSpell(speciesEntry->SummonSpellID, false);
+
+ battlePetMgr->AddPet(speciesId, creatureId, breed, quality, level);
+ plr->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
+ m_CastItem = nullptr;
+}
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index af1d7134663..d6cceac46fb 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -27,6 +27,7 @@
#include "AuctionHouseMgr.h"
#include "BattlefieldMgr.h"
#include "BattlegroundMgr.h"
+#include "BattlePetMgr.h"
#include "CalendarMgr.h"
#include "Channel.h"
#include "CharacterDatabaseCleaner.h"
@@ -2026,6 +2027,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading realm names...");
sObjectMgr->LoadRealmNames();
+ TC_LOG_INFO("server.loading", "Loading battle pets info...");
+ BattlePetMgr::Initialize();
+
uint32 startupDuration = GetMSTimeDiffToNow(startupBegin);
TC_LOG_INFO("server.worldserver", "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000));