TrinityCore
BattlePetMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "BattlePetMgr.h"
19#include "DB2Stores.h"
20#include "Containers.h"
21#include "Creature.h"
22#include "DatabaseEnv.h"
23#include "GameTables.h"
24#include "GameTime.h"
25#include "Item.h"
26#include "Log.h"
27#include "ObjectAccessor.h"
28#include "ObjectMgr.h"
29#include "Player.h"
30#include "Realm.h"
31#include "Util.h"
32#include "World.h"
33#include "WorldSession.h"
34
35namespace BattlePets
36{
37namespace
38{
39std::unordered_map<uint16 /*BreedID*/, std::unordered_map<BattlePetState /*state*/, int32 /*value*/, std::hash<std::underlying_type<BattlePetState>::type> >> _battlePetBreedStates;
40std::unordered_map<uint32 /*SpeciesID*/, std::unordered_map<BattlePetState /*state*/, int32 /*value*/, std::hash<std::underlying_type<BattlePetState>::type> >> _battlePetSpeciesStates;
41std::unordered_map<uint32 /*CreatureID*/, BattlePetSpeciesEntry const*> _battlePetSpeciesByCreature;
42std::unordered_map<uint32 /*SpellID*/, BattlePetSpeciesEntry const*> _battlePetSpeciesBySpell;
43std::unordered_map<uint32 /*SpeciesID*/, std::unordered_set<uint8 /*breed*/>> _availableBreedsPerSpecies;
44std::unordered_map<uint32 /*SpeciesID*/, uint8 /*quality*/> _defaultQualityPerSpecies;
45}
46
48{
49 float health = 0.0f;
50 float power = 0.0f;
51 float speed = 0.0f;
52
53 // get base breed stats
54 auto breedState = _battlePetBreedStates.find(PacketInfo.Breed);
55 if (breedState == _battlePetBreedStates.end()) // non existing breed id
56 return;
57
58 health = breedState->second[STATE_STAT_STAMINA];
59 power = breedState->second[STATE_STAT_POWER];
60 speed = breedState->second[STATE_STAT_SPEED];
61
62 // modify stats depending on species - not all pets have this
63 auto speciesState = _battlePetSpeciesStates.find(PacketInfo.Species);
64 if (speciesState != _battlePetSpeciesStates.end())
65 {
66 health += speciesState->second[STATE_STAT_STAMINA];
67 power += speciesState->second[STATE_STAT_POWER];
68 speed += speciesState->second[STATE_STAT_SPEED];
69 }
70
71 // modify stats by quality
72 for (BattlePetBreedQualityEntry const* battlePetBreedQuality : sBattlePetBreedQualityStore)
73 {
74 if (battlePetBreedQuality->QualityEnum == PacketInfo.Quality)
75 {
76 health *= battlePetBreedQuality->StateMultiplier;
77 power *= battlePetBreedQuality->StateMultiplier;
78 speed *= battlePetBreedQuality->StateMultiplier;
79 break;
80 }
81 // TOOD: add check if pet has existing quality
82 }
83
84 // scale stats depending on level
85 health *= PacketInfo.Level;
86 power *= PacketInfo.Level;
87 speed *= PacketInfo.Level;
88
89 // set stats
90 // round, ceil or floor? verify this
91 PacketInfo.MaxHealth = uint32((round(health / 20) + 100));
92 PacketInfo.Power = uint32(round(power / 100));
93 PacketInfo.Speed = uint32(round(speed / 100));
94}
95
97{
98 if (QueryResult result = LoginDatabase.Query("SELECT MAX(guid) FROM battle_pets"))
99 sObjectMgr->GetGenerator<HighGuid::BattlePet>().Set((*result)[0].GetUInt64() + 1);
100
101 for (BattlePetSpeciesEntry const* battlePetSpecies : sBattlePetSpeciesStore)
102 if (uint32 creatureId = battlePetSpecies->CreatureID)
103 _battlePetSpeciesByCreature[creatureId] = battlePetSpecies;
104
105 for (BattlePetBreedStateEntry const* battlePetBreedState : sBattlePetBreedStateStore)
106 _battlePetBreedStates[battlePetBreedState->BattlePetBreedID][BattlePetState(battlePetBreedState->BattlePetStateID)] = battlePetBreedState->Value;
107
108 for (BattlePetSpeciesStateEntry const* battlePetSpeciesState : sBattlePetSpeciesStateStore)
109 _battlePetSpeciesStates[battlePetSpeciesState->BattlePetSpeciesID][BattlePetState(battlePetSpeciesState->BattlePetStateID)] = battlePetSpeciesState->Value;
110
113}
114
116{
117 QueryResult result = WorldDatabase.Query("SELECT speciesId, breedId FROM battle_pet_breeds");
118 if (!result)
119 {
120 TC_LOG_INFO("server.loading", ">> Loaded 0 battle pet breeds. DB table `battle_pet_breeds` is empty.");
121 return;
122 }
123
124 uint32 count = 0;
125 do
126 {
127 Field* fields = result->Fetch();
128 uint32 speciesId = fields[0].GetUInt32();
129 uint16 breedId = fields[1].GetUInt16();
130
131 if (!sBattlePetSpeciesStore.LookupEntry(speciesId))
132 {
133 TC_LOG_ERROR("sql.sql", "Non-existing BattlePetSpecies.db2 entry {} was referenced in `battle_pet_breeds` by row ({}, {}).", speciesId, speciesId, breedId);
134 continue;
135 }
136
137 // TODO: verify breed id (3 - 12 (male) or 3 - 22 (male and female)) if needed
138
139 _availableBreedsPerSpecies[speciesId].insert(breedId);
140 ++count;
141 } while (result->NextRow());
142
143 TC_LOG_INFO("server.loading", ">> Loaded {} battle pet breeds.", count);
144}
145
147{
148 QueryResult result = WorldDatabase.Query("SELECT speciesId, quality FROM battle_pet_quality");
149 if (!result)
150 {
151 TC_LOG_INFO("server.loading", ">> Loaded 0 battle pet qualities. DB table `battle_pet_quality` is empty.");
152 return;
153 }
154
155 do
156 {
157 Field* fields = result->Fetch();
158 uint32 speciesId = fields[0].GetUInt32();
159 uint8 quality = fields[1].GetUInt8();
160
161 BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(speciesId);
162 if (!battlePetSpecies)
163 {
164 TC_LOG_ERROR("sql.sql", "Non-existing BattlePetSpecies.db2 entry {} was referenced in `battle_pet_quality` by row ({}, {}).", speciesId, speciesId, quality);
165 continue;
166 }
167
169 {
170 TC_LOG_ERROR("sql.sql", "BattlePetSpecies.db2 entry {} was referenced in `battle_pet_quality` with non-existing quality {}).", speciesId, quality);
171 continue;
172 }
173
174 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::WellKnown) && quality > AsUnderlyingType(BattlePetBreedQuality::Rare))
175 {
176 TC_LOG_ERROR("sql.sql", "Learnable BattlePetSpecies.db2 entry {} was referenced in `battle_pet_quality` with invalid quality {}. Maximum allowed quality is BattlePetBreedQuality::Rare.", speciesId, quality);
177 continue;
178 }
179
180 _defaultQualityPerSpecies[speciesId] = quality;
181 } while (result->NextRow());
182
183 TC_LOG_INFO("server.loading", ">> Loaded {} battle pet qualities.", uint32(_defaultQualityPerSpecies.size()));
184}
185
187{
188 _battlePetSpeciesBySpell[spellId] = speciesEntry;
189}
190
192{
193 return Trinity::Containers::MapGetValuePtr(_battlePetSpeciesByCreature, creatureId);
194}
195
197{
198 return Trinity::Containers::MapGetValuePtr(_battlePetSpeciesBySpell, spellId);
199}
200
202{
203 auto itr = _availableBreedsPerSpecies.find(species);
204 if (itr == _availableBreedsPerSpecies.end())
205 return 3; // default B/B
206
208}
209
211{
212 auto itr = _defaultQualityPerSpecies.find(species);
213 if (itr == _defaultQualityPerSpecies.end())
214 return BattlePetBreedQuality::Poor; // Default
215
216 return BattlePetBreedQuality(itr->second);
217}
218
220{
221 if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(speciesEntry->CreatureID))
222 if (!speciesEntry->GetFlags().HasFlag(BattlePetSpeciesFlags::RandomDisplay))
223 if (CreatureModel const* creatureModel = creatureTemplate->GetRandomValidModel())
224 return creatureModel->CreatureDisplayID;
225
226 return 0;
227}
228
230{
231 _owner = owner;
232 for (uint8 i = 0; i < AsUnderlyingType(BattlePetSlot::Count); ++i)
233 {
235 slot.Index = i;
236 _slots.push_back(slot);
237 }
238}
239
241{
242 if (pets)
243 {
244 do
245 {
246 Field* fields = pets->Fetch();
247 uint32 species = fields[1].GetUInt32();
248 ObjectGuid ownerGuid = !fields[11].IsNull() ? ObjectGuid::Create<HighGuid::Player>(fields[11].GetInt64()) : ObjectGuid::Empty;
249
250 if (BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(species))
251 {
252 if (speciesEntry->GetFlags().HasFlag(BattlePetSpeciesFlags::NotAccountWide))
253 {
254 if (ownerGuid.IsEmpty())
255 {
256 TC_LOG_ERROR("misc", "Battlenet account with id {} has battle pet of species {} with BattlePetSpeciesFlags::NotAccountWide but no owner", _owner->GetBattlenetAccountId(), species);
257 continue;
258 }
259 }
260 else
261 {
262 if (!ownerGuid.IsEmpty())
263 {
264 TC_LOG_ERROR("misc", "Battlenet account with id {} has battle pet of species {} without BattlePetSpeciesFlags::NotAccountWide but with owner", _owner->GetBattlenetAccountId(), species);
265 continue;
266 }
267 }
268
269 if (HasMaxPetCount(speciesEntry, ownerGuid))
270 {
271 if (ownerGuid.IsEmpty())
272 TC_LOG_ERROR("misc", "Battlenet account with id {} has more than maximum battle pets of species {}", _owner->GetBattlenetAccountId(), species);
273 else
274 TC_LOG_ERROR("misc", "Battlenet account with id {} has more than maximum battle pets of species {} for player {}", _owner->GetBattlenetAccountId(), species, ownerGuid.ToString());
275
276 continue;
277 }
278
279 BattlePet pet;
280 pet.PacketInfo.Guid = ObjectGuid::Create<HighGuid::BattlePet>(fields[0].GetUInt64());
281 pet.PacketInfo.Species = species;
282 pet.PacketInfo.Breed = fields[2].GetUInt16();
283 pet.PacketInfo.DisplayID = fields[3].GetUInt32();
284 pet.PacketInfo.Level = fields[4].GetUInt16();
285 pet.PacketInfo.Exp = fields[5].GetUInt16();
286 pet.PacketInfo.Health = fields[6].GetUInt32();
287 pet.PacketInfo.Quality = fields[7].GetUInt8();
288 pet.PacketInfo.Flags = fields[8].GetUInt16();
289 pet.PacketInfo.Name = fields[9].GetString();
290 pet.NameTimestamp = fields[10].GetInt64();
291 pet.PacketInfo.CreatureID = speciesEntry->CreatureID;
292
293 if (!fields[12].IsNull())
294 {
295 pet.DeclinedName = std::make_unique<DeclinedName>();
296 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
297 pet.DeclinedName->name[i] = fields[12 + i].GetString();
298 }
299
300 if (!ownerGuid.IsEmpty())
301 {
302 pet.PacketInfo.OwnerInfo.emplace();
303 pet.PacketInfo.OwnerInfo->Guid = ownerGuid;
304 pet.PacketInfo.OwnerInfo->PlayerVirtualRealm = pet.PacketInfo.OwnerInfo->PlayerNativeRealm = GetVirtualRealmAddress();
305 }
306
308 pet.CalculateStats();
309 _pets[pet.PacketInfo.Guid.GetCounter()] = std::move(pet);
310 }
311 } while (pets->NextRow());
312 }
313
314 if (slots)
315 {
316 uint8 i = 0; // slots->GetRowCount() should equal MAX_BATTLE_PET_SLOTS
317
318 do
319 {
320 Field* fields = slots->Fetch();
321 _slots[i].Index = fields[0].GetUInt8();
322 auto itr = _pets.find(fields[1].GetUInt64());
323 if (itr != _pets.end())
324 _slots[i].Pet = itr->second.PacketInfo;
325 _slots[i].Locked = fields[2].GetBool();
326 i++;
327 } while (slots->NextRow());
328 }
329}
330
332{
333 LoginDatabasePreparedStatement* stmt = nullptr;
334
335 for (auto itr = _pets.begin(); itr != _pets.end();)
336 {
337 switch (itr->second.SaveInfo)
338 {
339 case BATTLE_PET_NEW:
340 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BATTLE_PETS);
341 stmt->setUInt64(0, itr->first);
343 stmt->setUInt32(2, itr->second.PacketInfo.Species);
344 stmt->setUInt16(3, itr->second.PacketInfo.Breed);
345 stmt->setUInt32(4, itr->second.PacketInfo.DisplayID);
346 stmt->setUInt16(5, itr->second.PacketInfo.Level);
347 stmt->setUInt16(6, itr->second.PacketInfo.Exp);
348 stmt->setUInt32(7, itr->second.PacketInfo.Health);
349 stmt->setUInt8(8, itr->second.PacketInfo.Quality);
350 stmt->setUInt16(9, itr->second.PacketInfo.Flags);
351 stmt->setString(10, itr->second.PacketInfo.Name);
352 stmt->setInt64(11, itr->second.NameTimestamp);
353 if (itr->second.PacketInfo.OwnerInfo)
354 {
355 stmt->setInt64(12, itr->second.PacketInfo.OwnerInfo->Guid.GetCounter());
356 stmt->setInt32(13, realm.Id.Realm);
357 }
358 else
359 {
360 stmt->setNull(12);
361 stmt->setNull(13);
362 }
363
364 trans->Append(stmt);
365
366 if (itr->second.DeclinedName)
367 {
368 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BATTLE_PET_DECLINED_NAME);
369 stmt->setUInt64(0, itr->first);
370
371 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; i++)
372 stmt->setString(i + 1, itr->second.DeclinedName->name[i]);
373
374 trans->Append(stmt);
375 }
376
377 itr->second.SaveInfo = BATTLE_PET_UNCHANGED;
378 ++itr;
379 break;
381 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BATTLE_PETS);
382 stmt->setUInt16(0, itr->second.PacketInfo.Level);
383 stmt->setUInt16(1, itr->second.PacketInfo.Exp);
384 stmt->setUInt32(2, itr->second.PacketInfo.Health);
385 stmt->setUInt8(3, itr->second.PacketInfo.Quality);
386 stmt->setUInt16(4, itr->second.PacketInfo.Flags);
387 stmt->setString(5, itr->second.PacketInfo.Name);
388 stmt->setInt64(6, itr->second.NameTimestamp);
390 stmt->setUInt64(8, itr->first);
391 trans->Append(stmt);
392
393 stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BATTLE_PET_DECLINED_NAME);
394 stmt->setUInt64(0, itr->first);
395 trans->Append(stmt);
396
397 if (itr->second.DeclinedName)
398 {
399 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BATTLE_PET_DECLINED_NAME);
400 stmt->setUInt64(0, itr->first);
401
402 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; i++)
403 stmt->setString(i + 1, itr->second.DeclinedName->name[i]);
404
405 trans->Append(stmt);
406 }
407
408 itr->second.SaveInfo = BATTLE_PET_UNCHANGED;
409 ++itr;
410 break;
412 stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BATTLE_PET_DECLINED_NAME);
413 stmt->setUInt64(0, itr->first);
414 trans->Append(stmt);
415
416 stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BATTLE_PETS);
418 stmt->setUInt64(1, itr->first);
419 trans->Append(stmt);
420
421 itr = _pets.erase(itr);
422 break;
423 default:
424 ++itr;
425 break;
426 }
427 }
428
429 stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BATTLE_PET_SLOTS);
431 trans->Append(stmt);
432
434 {
435 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BATTLE_PET_SLOTS);
436 stmt->setUInt8(0, slot.Index);
438 stmt->setUInt64(2, slot.Pet.Guid.GetCounter());
439 stmt->setBool(3, slot.Locked);
440 trans->Append(stmt);
441 }
442}
443
445{
447}
448
449void BattlePetMgr::AddPet(uint32 species, uint32 display, uint16 breed, BattlePetBreedQuality quality, uint16 level /*= 1*/)
450{
451 BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(species);
452 if (!battlePetSpecies) // should never happen
453 return;
454
455 if (!battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::WellKnown)) // Not learnable
456 return;
457
458 BattlePet pet;
459 pet.PacketInfo.Guid = ObjectGuid::Create<HighGuid::BattlePet>(sObjectMgr->GetGenerator<HighGuid::BattlePet>().Generate());
460 pet.PacketInfo.Species = species;
461 pet.PacketInfo.CreatureID = battlePetSpecies->CreatureID;
462 pet.PacketInfo.DisplayID = display;
463 pet.PacketInfo.Level = level;
464 pet.PacketInfo.Exp = 0;
465 pet.PacketInfo.Flags = 0;
466 pet.PacketInfo.Breed = breed;
467 pet.PacketInfo.Quality = AsUnderlyingType(quality);
468 pet.PacketInfo.Name = "";
469 pet.CalculateStats();
471
472 Player* player = _owner->GetPlayer();
473 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::NotAccountWide))
474 {
475 pet.PacketInfo.OwnerInfo.emplace();
476 pet.PacketInfo.OwnerInfo->Guid = player->GetGUID();
477 pet.PacketInfo.OwnerInfo->PlayerVirtualRealm = pet.PacketInfo.OwnerInfo->PlayerNativeRealm = GetVirtualRealmAddress();
478 }
479
481
482 _pets[pet.PacketInfo.Guid.GetCounter()] = std::move(pet);
483
484 std::vector<std::reference_wrapper<BattlePet>> updates;
485 updates.push_back(std::ref(pet));
486 SendUpdates(std::move(updates), true);
487
490}
491
493{
494 if (!HasJournalLock())
495 return;
496
497 BattlePet* pet = GetPet(guid);
498 if (!pet)
499 return;
500
502}
503
505{
506 BattlePet* pet = GetPet(guid);
507 if (!pet)
508 return;
509
510 pet->PacketInfo.Flags &= ~AsUnderlyingType(BattlePetDbFlags::FanfareNeeded);
511
512 if (pet->SaveInfo != BATTLE_PET_NEW)
514}
515
516void BattlePetMgr::ModifyName(ObjectGuid guid, std::string const& name, std::unique_ptr<DeclinedName> declinedName)
517{
518 if (!HasJournalLock())
519 return;
520
521 BattlePet* pet = GetPet(guid);
522 if (!pet)
523 return;
524
525 pet->PacketInfo.Name = name;
527
528 pet->DeclinedName = std::move(declinedName);
529
530 if (pet->SaveInfo != BATTLE_PET_NEW)
532
533 // Update the timestamp if the battle pet is summoned
534 if (Creature* summonedBattlePet = _owner->GetPlayer()->GetSummonedBattlePet())
535 if (summonedBattlePet->GetBattlePetCompanionGUID() == guid)
536 summonedBattlePet->SetBattlePetCompanionNameTimestamp(pet->NameTimestamp);
537}
538
540{
542 if (slot.Pet.Guid == guid)
543 return true;
544
545 return false;
546}
547
548uint8 BattlePetMgr::GetPetCount(BattlePetSpeciesEntry const* battlePetSpecies, ObjectGuid ownerGuid) const
549{
550 return uint8(std::count_if(_pets.begin(), _pets.end(), [battlePetSpecies, ownerGuid](std::pair<uint64 const, BattlePet> const& pet)
551 {
552 if (pet.second.PacketInfo.Species != battlePetSpecies->ID)
553 return false;
554
555 if (pet.second.SaveInfo == BATTLE_PET_REMOVED)
556 return false;
557
558 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::NotAccountWide))
559 if (!ownerGuid.IsEmpty() && pet.second.PacketInfo.OwnerInfo)
560 if (pet.second.PacketInfo.OwnerInfo->Guid != ownerGuid)
561 return false;
562
563 return true;
564 }));
565}
566
567bool BattlePetMgr::HasMaxPetCount(BattlePetSpeciesEntry const* battlePetSpecies, ObjectGuid ownerGuid) const
568{
569 uint8 maxPetsPerSpecies = battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::LegacyAccountUnique) ? 1 : DEFAULT_MAX_BATTLE_PETS_PER_SPECIES;
570
571 return GetPetCount(battlePetSpecies, ownerGuid) >= maxPetsPerSpecies;
572}
573
575{
576 std::set<uint32> speciesIds;
577 std::transform(_pets.begin(), _pets.end(), std::inserter(speciesIds, speciesIds.end()), [](std::pair<uint64 const, BattlePet> const& pet)
578 {
579 return pet.second.PacketInfo.Species;
580 });
581 return speciesIds.size();
582}
583
585{
586 if (slot >= BattlePetSlot::Count)
587 return;
588
589 uint8 slotIndex = AsUnderlyingType(slot);
590 if (!_slots[slotIndex].Locked)
591 return;
592
593 _slots[slotIndex].Locked = false;
594
596 updates.Slots.push_back(_slots[slotIndex]);
597 updates.AutoSlotted = false; // what's this?
598 updates.NewSlot = true; // causes the "new slot unlocked" bubble to appear
599 _owner->SendPacket(updates.Write());
600}
601
603{
604 uint16 level = 0;
605 for (auto& pet : _pets)
606 if (pet.second.SaveInfo != BATTLE_PET_REMOVED)
607 level = std::max(level, pet.second.PacketInfo.Level);
608
609 return level;
610}
611
613{
614 if (!HasJournalLock())
615 return;
616
617 BattlePet* pet = GetPet(guid);
618 if (!pet)
619 return;
620
621 if (BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species))
622 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::NotTradable))
623 return;
624
625 if (IsPetInSlot(guid))
626 return;
627
628 if (pet->PacketInfo.Health < pet->PacketInfo.MaxHealth)
629 return;
630
631 ItemPosCountVec dest;
632
634 return;
635
637 if (!item)
638 return;
639
644
645 _owner->GetPlayer()->SendNewItem(item, 1, true, false);
646
647 RemovePet(guid);
648
650 deletePet.PetGuid = guid;
651 _owner->SendPacket(deletePet.Write());
652
653 // Battle pet despawns if it's summoned
654 Player* player = _owner->GetPlayer();
655 if (Creature* summonedBattlePet = player->GetSummonedBattlePet())
656 {
657 if (summonedBattlePet->GetBattlePetCompanionGUID() == guid)
658 {
659 summonedBattlePet->DespawnOrUnsummon();
660 player->SetBattlePetData(nullptr);
661 }
662 }
663}
664
666{
667 if (!HasJournalLock())
668 return;
669
670 BattlePet* pet = GetPet(guid);
671 if (!pet)
672 return;
673
674 if (quality > BattlePetBreedQuality::Rare)
675 return;
676
677 if (BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species))
678 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::CantBattle))
679 return;
680
681 uint8 qualityValue = AsUnderlyingType(quality);
682 if (pet->PacketInfo.Quality >= qualityValue)
683 return;
684
685 pet->PacketInfo.Quality = qualityValue;
686 pet->CalculateStats();
688
689 if (pet->SaveInfo != BATTLE_PET_NEW)
691
692 std::vector<std::reference_wrapper<BattlePet>> updates;
693 updates.push_back(std::ref(*pet));
694 SendUpdates(std::move(updates), false);
695
696 // UF::PlayerData::CurrentBattlePetBreedQuality isn't updated (Intended)
697 // _owner->GetPlayer()->SetCurrentBattlePetBreedQuality(qualityValue);
698}
699
701{
702 if (!HasJournalLock())
703 return;
704
705 BattlePet* pet = GetPet(guid);
706 if (!pet)
707 return;
708
709 if (xp <= 0 || xpSource >= BattlePetXpSource::Count)
710 return;
711
712 if (BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species))
713 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::CantBattle))
714 return;
715
716 uint16 level = pet->PacketInfo.Level;
717 if (level >= MAX_BATTLE_PET_LEVEL)
718 return;
719
720 GtBattlePetXPEntry const* xpEntry = sBattlePetXPGameTable.GetRow(level);
721 if (!xpEntry)
722 return;
723
724 Player* player = _owner->GetPlayer();
725 uint16 nextLevelXp = uint16(GetBattlePetXPPerLevel(xpEntry));
726
727 if (xpSource == BattlePetXpSource::PetBattle)
729
730 xp += pet->PacketInfo.Exp;
731
732 while (xp >= nextLevelXp && level < MAX_BATTLE_PET_LEVEL)
733 {
734 xp -= nextLevelXp;
735
736 xpEntry = sBattlePetXPGameTable.GetRow(++level);
737 if (!xpEntry)
738 return;
739
740 nextLevelXp = uint16(GetBattlePetXPPerLevel(xpEntry));
741
743 if (xpSource == BattlePetXpSource::PetBattle)
745 }
746
747 pet->PacketInfo.Level = level;
748 pet->PacketInfo.Exp = level < MAX_BATTLE_PET_LEVEL ? xp : 0;
749 pet->CalculateStats();
751
752 if (pet->SaveInfo != BATTLE_PET_NEW)
754
755 std::vector<std::reference_wrapper<BattlePet>> updates;
756 updates.push_back(std::ref(*pet));
757 SendUpdates(std::move(updates), false);
758}
759
761{
762 if (!HasJournalLock())
763 return;
764
765 BattlePet* pet = GetPet(guid);
766 if (!pet)
767 return;
768
769 if (BattlePetSpeciesEntry const* battlePetSpecies = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species))
770 if (battlePetSpecies->GetFlags().HasFlag(BattlePetSpeciesFlags::CantBattle))
771 return;
772
773 uint16 level = pet->PacketInfo.Level;
774 if (level >= MAX_BATTLE_PET_LEVEL)
775 return;
776
777 while (grantedLevels > 0 && level < MAX_BATTLE_PET_LEVEL)
778 {
779 ++level;
780 --grantedLevels;
781
783 }
784
785 pet->PacketInfo.Level = level;
786 if (level >= MAX_BATTLE_PET_LEVEL)
787 pet->PacketInfo.Exp = 0;
788 pet->CalculateStats();
790
791 if (pet->SaveInfo != BATTLE_PET_NEW)
793
794 std::vector<std::reference_wrapper<BattlePet>> updates;
795 updates.push_back(std::ref(*pet));
796 SendUpdates(std::move(updates), false);
797}
798
800{
801 // TODO: After each Pet Battle, any injured companion will automatically
802 // regain 50 % of the damage that was taken during combat
803 std::vector<std::reference_wrapper<BattlePet>> updates;
804
805 for (auto& pet : _pets)
806 if (pet.second.PacketInfo.Health != pet.second.PacketInfo.MaxHealth)
807 {
808 pet.second.PacketInfo.Health += CalculatePct(pet.second.PacketInfo.MaxHealth, pct);
809 // don't allow Health to be greater than MaxHealth
810 pet.second.PacketInfo.Health = std::min(pet.second.PacketInfo.Health, pet.second.PacketInfo.MaxHealth);
811 if (pet.second.SaveInfo != BATTLE_PET_NEW)
812 pet.second.SaveInfo = BATTLE_PET_CHANGED;
813 updates.push_back(std::ref(pet.second));
814 }
815
816 SendUpdates(std::move(updates), false);
817}
818
820{
821 BattlePet* pet = GetPet(guid);
822 if (!pet)
823 return;
824
825 Player* player = _owner->GetPlayer();
826
827 // Update battle pet related update fields
828 if (Creature* summonedBattlePet = player->GetSummonedBattlePet())
829 {
830 if (summonedBattlePet->GetBattlePetCompanionGUID() == guid)
831 {
832 summonedBattlePet->SetWildBattlePetLevel(pet->PacketInfo.Level);
833 player->SetBattlePetData(pet);
834 }
835 }
836}
837
839{
840 BattlePet* pet = GetPet(guid);
841 if (!pet)
842 return;
843
844 BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(pet->PacketInfo.Species);
845 if (!speciesEntry)
846 return;
847
848 Player* player = _owner->GetPlayer();
849 player->SetBattlePetData(pet);
850
852 uint32 summonSpellId = speciesEntry->SummonSpellID;
853 if (!summonSpellId)
854 {
855 summonSpellId = uint32(SPELL_SUMMON_BATTLE_PET);
856 args.AddSpellBP0(speciesEntry->CreatureID);
857 }
858 player->CastSpell(_owner->GetPlayer(), summonSpellId, args);
859}
860
862{
863 Player* player = _owner->GetPlayer();
864 if (Creature* summonedBattlePet = player->GetSummonedBattlePet())
865 {
866 summonedBattlePet->DespawnOrUnsummon();
867 player->SetBattlePetData(nullptr);
868 }
869}
870
872{
873 if (!HasJournalLock())
875
877 battlePetJournal.Trap = _trapLevel;
878 battlePetJournal.HasJournalLock = _hasJournalLock;
879
880 for (auto& pet : _pets)
881 if (pet.second.SaveInfo != BATTLE_PET_REMOVED)
882 if (!pet.second.PacketInfo.OwnerInfo || pet.second.PacketInfo.OwnerInfo->Guid == _owner->GetPlayer()->GetGUID())
883 battlePetJournal.Pets.push_back(std::ref(pet.second.PacketInfo));
884
885 battlePetJournal.Slots.reserve(_slots.size());
886 std::transform(_slots.begin(), _slots.end(), std::back_inserter(battlePetJournal.Slots), [](WorldPackets::BattlePet::BattlePetSlot& slot) { return std::ref(slot); });
887 _owner->SendPacket(battlePetJournal.Write());
888}
889
890void BattlePetMgr::SendUpdates(std::vector<std::reference_wrapper<BattlePet>> pets, bool petAdded)
891{
893 for (BattlePet& pet : pets)
894 updates.Pets.push_back(std::ref(pet.PacketInfo));
895
896 updates.PetAdded = petAdded;
897 _owner->SendPacket(updates.Write());
898}
899
901{
903 battlePetError.Result = AsUnderlyingType(error);
904 battlePetError.CreatureID = creatureId;
905 _owner->SendPacket(battlePetError.Write());
906}
907
909{
911 ToggleJournalLock(true);
912
913 if (HasJournalLock())
914 {
916 _owner->SendPacket(battlePetJournalLockAcquired.Write());
917 }
918 else
919 {
921 _owner->SendPacket(BattlePetJournalLockDenied.Write());
922 }
923}
924
926{
927 return sWorld->IsBattlePetJournalLockAcquired(_owner->GetBattlenetAccountGUID());
928}
929}
DB2Storage< BattlePetBreedQualityEntry > sBattlePetBreedQualityStore("BattlePetBreedQuality.db2", &BattlePetBreedQualityLoadInfo::Instance)
DB2Storage< BattlePetBreedStateEntry > sBattlePetBreedStateStore("BattlePetBreedState.db2", &BattlePetBreedStateLoadInfo::Instance)
DB2Storage< BattlePetSpeciesStateEntry > sBattlePetSpeciesStateStore("BattlePetSpeciesState.db2", &BattlePetSpeciesStateLoadInfo::Instance)
DB2Storage< BattlePetSpeciesEntry > sBattlePetSpeciesStore("BattlePetSpecies.db2", &BattlePetSpeciesLoadInfo::Instance)
SQLTransaction< LoginDatabaseConnection > LoginDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
Definition: DatabaseEnv.cpp:22
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
uint8_t uint8
Definition: Define.h:144
int32_t int32
Definition: Define.h:138
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
GameTable< GtBattlePetXPEntry > sBattlePetXPGameTable
Definition: GameTables.cpp:32
float GetBattlePetXPPerLevel(GtBattlePetXPEntry const *row)
Definition: GameTables.h:304
@ EQUIP_ERR_OK
Definition: ItemDefines.h:26
@ ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID
Definition: ItemDefines.h:217
@ ITEM_MODIFIER_BATTLE_PET_BREED_DATA
Definition: ItemDefines.h:215
@ ITEM_MODIFIER_BATTLE_PET_SPECIES_ID
Definition: ItemDefines.h:214
@ ITEM_MODIFIER_BATTLE_PET_LEVEL
Definition: ItemDefines.h:216
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
@ LOGIN_INS_BATTLE_PET_SLOTS
@ LOGIN_INS_BATTLE_PETS
@ LOGIN_DEL_BATTLE_PETS
@ LOGIN_DEL_BATTLE_PET_DECLINED_NAME
@ LOGIN_INS_BATTLE_PET_DECLINED_NAME
@ LOGIN_UPD_BATTLE_PETS
@ LOGIN_DEL_BATTLE_PET_SLOTS
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::vector< ItemPosCount > ItemPosCountVec
Definition: Player.h:750
@ SPELL_AURA_MOD_BATTLE_PET_XP_PCT
#define MAX_DECLINED_NAME_CASES
Definition: UnitDefines.h:484
@ NULL_BAG
Definition: Unit.h:62
@ NULL_SLOT
Definition: Unit.h:63
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
T CalculatePct(T base, U pct)
Definition: Util.h:72
std::vector< WorldPackets::BattlePet::BattlePetSlot > _slots
Definition: BattlePetMgr.h:211
void GrantBattlePetExperience(ObjectGuid guid, uint16 xp, BattlePetXpSource xpSource)
void HealBattlePetsPct(uint8 pct)
static uint16 RollPetBreed(uint32 species)
void RemovePet(ObjectGuid guid)
uint32 GetPetUniqueSpeciesCount() const
void ToggleJournalLock(bool lock)
Definition: BattlePetMgr.h:202
static void LoadAvailablePetBreeds()
bool IsJournalLockAcquired() const
static void AddBattlePetSpeciesBySpell(uint32 spellId, BattlePetSpeciesEntry const *speciesEntry)
uint16 GetMaxPetLevel() const
void ClearFanfare(ObjectGuid guid)
static BattlePetSpeciesEntry const * GetBattlePetSpeciesByCreature(uint32 creatureId)
void SummonPet(ObjectGuid guid)
BattlePetMgr(WorldSession *owner)
void ModifyName(ObjectGuid guid, std::string const &name, std::unique_ptr< DeclinedName > declinedName)
static uint32 SelectPetDisplay(BattlePetSpeciesEntry const *speciesEntry)
void UnlockSlot(BattlePetSlot slot)
uint8 GetPetCount(BattlePetSpeciesEntry const *battlePetSpecies, ObjectGuid ownerGuid) const
static BattlePetSpeciesEntry const * GetBattlePetSpeciesBySpell(uint32 spellId)
void AddPet(uint32 species, uint32 display, uint16 breed, BattlePetBreedQuality quality, uint16 level=1)
void SendUpdates(std::vector< std::reference_wrapper< BattlePet > > pets, bool petAdded)
std::unordered_map< uint64, BattlePet > _pets
Definition: BattlePetMgr.h:210
void UpdateBattlePetData(ObjectGuid guid)
BattlePet * GetPet(ObjectGuid guid)
static void LoadDefaultPetQualities()
bool HasJournalLock() const
Definition: BattlePetMgr.h:201
void CageBattlePet(ObjectGuid guid)
void GrantBattlePetLevel(ObjectGuid guid, uint16 grantedLevels)
bool HasMaxPetCount(BattlePetSpeciesEntry const *battlePetSpecies, ObjectGuid ownerGuid) const
void SendError(BattlePetError error, uint32 creatureId)
void ChangeBattlePetQuality(ObjectGuid guid, BattlePetBreedQuality quality)
bool IsPetInSlot(ObjectGuid guid)
static BattlePetBreedQuality GetDefaultPetQuality(uint32 species)
void LoadFromDB(PreparedQueryResult pets, PreparedQueryResult slots)
void SaveToDB(LoginDatabaseTransaction trans)
Class used to access individual fields of database query result.
Definition: Field.h:90
uint8 GetUInt8() const
Definition: Field.cpp:30
std::string GetString() const
Definition: Field.cpp:118
bool IsNull() const
Definition: Field.h:125
int64 GetInt64() const
Definition: Field.cpp:86
uint16 GetUInt16() const
Definition: Field.cpp:46
bool GetBool() const
Definition: Field.h:98
uint32 GetUInt32() const
Definition: Field.cpp:62
Definition: Item.h:170
void SetModifier(ItemModifier modifier, uint32 value)
Definition: Item.cpp:2436
LowType GetCounter() const
Definition: ObjectGuid.h:293
static ObjectGuid const Empty
Definition: ObjectGuid.h:274
bool IsEmpty() const
Definition: ObjectGuid.h:319
std::string ToString() const
Definition: ObjectGuid.cpp:554
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
Item * StoreNewItem(ItemPosCountVec const &pos, uint32 itemId, bool update, ItemRandomBonusListId randomBonusListId=0, GuidSet const &allowedLooters=GuidSet(), ItemContext context=ItemContext::NONE, std::vector< int32 > const *bonusListIDs=nullptr, bool addToCollection=true)
Definition: Player.cpp:11537
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition: Player.cpp:26767
void SetBattlePetData(BattlePets::BattlePet const *pet=nullptr)
Definition: Player.cpp:21659
Creature * GetSummonedBattlePet() const
Definition: Player.cpp:21650
InventoryResult CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 item, uint32 count, uint32 *no_space_count=nullptr) const
Definition: Player.cpp:10032
void SendNewItem(Item *item, uint32 quantity, bool received, bool created, bool broadcast=false, uint32 dungeonEncounterId=0)
Definition: Player.cpp:13990
void setInt32(const uint8 index, const int32 value)
void setUInt8(const uint8 index, const uint8 value)
void setInt64(const uint8 index, const int64 value)
void setBool(const uint8 index, const bool value)
void setUInt32(const uint8 index, const uint32 value)
void setUInt16(const uint8 index, const uint16 value)
void setNull(const uint8 index)
void setString(const uint8 index, const std::string &value)
void setUInt64(const uint8 index, const uint64 value)
float GetTotalAuraMultiplier(AuraType auraType) const
Definition: Unit.cpp:4934
void SetWildBattlePetLevel(uint32 wildBattlePetLevel)
Definition: Unit.h:1250
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition: Object.cpp:2896
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
std::vector< std::reference_wrapper< BattlePet > > Pets
std::vector< std::reference_wrapper< BattlePetSlot > > Slots
WorldPacket const * Write() override
std::vector< std::reference_wrapper< BattlePet > > Pets
Player session in the World.
Definition: WorldSession.h:963
ObjectGuid GetBattlenetAccountGUID() const
Player * GetPlayer() const
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
uint32 GetBattlenetAccountId() const
#define sWorld
Definition: World.h:931
uint32 GetVirtualRealmAddress()
Definition: World.cpp:3968
Realm realm
Definition: World.cpp:3966
@ BATTLE_PET_UNCHANGED
Definition: BattlePetMgr.h:130
@ BATTLE_PET_REMOVED
Definition: BattlePetMgr.h:133
@ BATTLE_PET_CHANGED
Definition: BattlePetMgr.h:131
@ SPELL_SUMMON_BATTLE_PET
Definition: BattlePetMgr.h:38
@ DEFAULT_MAX_BATTLE_PETS_PER_SPECIES
Definition: BattlePetMgr.h:32
@ BATTLE_PET_CAGE_ITEM_ID
Definition: BattlePetMgr.h:33
static constexpr uint16 MAX_BATTLE_PET_LEVEL
Definition: BattlePetMgr.h:41
@ STATE_STAT_STAMINA
Definition: BattlePetMgr.h:110
time_t GetGameTime()
Definition: GameTime.cpp:44
auto SelectRandomContainerElement(C const &container) -> typename std::add_const< decltype(*std::begin(container))>::type &
Definition: Containers.h:109
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
EnumFlag< BattlePetSpeciesFlags > GetFlags() const
Definition: DB2Structure.h:487
WorldPackets::BattlePet::BattlePet PacketInfo
Definition: BattlePetMgr.h:140
std::unique_ptr<::DeclinedName > DeclinedName
Definition: BattlePetMgr.h:142
BattlePetSaveInfo SaveInfo
Definition: BattlePetMgr.h:143
CastSpellExtraArgs & AddSpellBP0(int32 val)
Definition: SpellDefines.h:475
Battlenet::RealmHandle Id
Definition: Realm.h:82
Optional< BattlePetOwnerInfo > OwnerInfo