aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/BattlePets/BattlePetMgr.cpp
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/game/BattlePets/BattlePetMgr.cpp
parente0fcb410b4d08119b7e72f154d7eed8cd2bd1e0f (diff)
Core/BattlePets: Basics for Battle Pets
Diffstat (limited to 'src/server/game/BattlePets/BattlePetMgr.cpp')
-rw-r--r--src/server/game/BattlePets/BattlePetMgr.cpp467
1 files changed, 467 insertions, 0 deletions
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());
+}