diff --git a/Source/Framework/Constants/BattlePetConst.cs b/Source/Framework/Constants/BattlePetConst.cs index 45a435e51..cde907569 100644 --- a/Source/Framework/Constants/BattlePetConst.cs +++ b/Source/Framework/Constants/BattlePetConst.cs @@ -72,4 +72,16 @@ namespace Framework.Constants New = 2, Removed = 3 } + + public enum BattlePetBreedQuality + { + Poor = 0, + Common = 1, + Uncommon = 2, + Rare = 3, + Epic = 4, + Legendary = 5, + + Max + } } diff --git a/Source/Game/BattlePets/BattlePetManager.cs b/Source/Game/BattlePets/BattlePetManager.cs index c7e2513b5..26bbb4e32 100644 --- a/Source/Game/BattlePets/BattlePetManager.cs +++ b/Source/Game/BattlePets/BattlePetManager.cs @@ -107,15 +107,26 @@ namespace Game.BattlePets do { uint speciesId = result.Read(0); - byte quality = result.Read(1); + BattlePetBreedQuality quality = (BattlePetBreedQuality)result.Read(1); - if (!CliDB.BattlePetSpeciesStorage.ContainsKey(speciesId)) + var battlePetSpecies = CliDB.BattlePetSpeciesStorage.LookupByKey(speciesId); + if (battlePetSpecies == null) { - Log.outError(LogFilter.Sql, "Non-existing BattlePetSpecies.db2 entry {0} was referenced in `battle_pet_quality` by row ({1}, {2}).", speciesId, speciesId, quality); + Log.outError(LogFilter.Sql, $"Non-existing BattlePetSpecies.db2 entry {speciesId} was referenced in `battle_pet_quality` by row ({speciesId}, {quality})."); continue; } - // TODO: verify quality (0 - 3 for player pets or 0 - 5 for both player and tamer pets) if needed + if (quality >= BattlePetBreedQuality.Max) + { + Log.outError(LogFilter.Sql, $"BattlePetSpecies.db2 entry {speciesId} was referenced in `battle_pet_quality` with non-existing quality {quality})."); + continue; + } + + if (battlePetSpecies.GetFlags().HasFlag(BattlePetSpeciesFlags.WellKnown) && quality > BattlePetBreedQuality.Rare) + { + Log.outError(LogFilter.Sql, $"Learnable BattlePetSpecies.db2 entry {speciesId} was referenced in `battle_pet_quality` with invalid quality {quality}. Maximum allowed quality is BattlePetBreedQuality::Rare."); + continue; + } _defaultQualityPerSpecies[speciesId] = quality; } while (result.NextRow()); @@ -132,10 +143,10 @@ namespace Game.BattlePets return list.SelectRandom(); } - public static byte GetDefaultPetQuality(uint species) + public static BattlePetBreedQuality GetDefaultPetQuality(uint species) { if (!_defaultQualityPerSpecies.ContainsKey(species)) - return 0; // default poor + return BattlePetBreedQuality.Poor; // Default return _defaultQualityPerSpecies[species]; } @@ -260,7 +271,7 @@ namespace Game.BattlePets public void AddPet(uint species, uint creatureId, ushort level = 1) { ushort breed = 3;// default B/B - byte quality = 0; + BattlePetBreedQuality quality = 0; if (_availableBreedsPerSpecies.ContainsKey(species)) breed = _availableBreedsPerSpecies[species].SelectRandom(); @@ -271,7 +282,7 @@ namespace Game.BattlePets AddPet(species, creatureId, breed, quality, level); } - public void AddPet(uint species, uint creatureId, ushort breed, byte quality, ushort level = 1) + public void AddPet(uint species, uint creatureId, ushort breed, BattlePetBreedQuality quality, ushort level = 1) { BattlePetSpeciesRecord battlePetSpecies = CliDB.BattlePetSpeciesStorage.LookupByKey(species); if (battlePetSpecies == null) // should never happen @@ -288,7 +299,7 @@ namespace Game.BattlePets pet.PacketInfo.Exp = 0; pet.PacketInfo.Flags = 0; pet.PacketInfo.Breed = breed; - pet.PacketInfo.Quality = quality; + pet.PacketInfo.Quality = (byte)quality; pet.PacketInfo.Name = ""; pet.CalculateStats(); pet.PacketInfo.Health = pet.PacketInfo.MaxHealth; @@ -456,6 +467,7 @@ namespace Game.BattlePets // TODO: set proper CreatureID for spell DEFAULT_SUMMON_BATTLE_PET_SPELL (default EffectMiscValueA is 40721 - Murkimus the Gladiator) _owner.GetPlayer().SetSummonedBattlePetGUID(guid); + _owner.GetPlayer().SetCurrentBattlePetBreedQuality(pet.PacketInfo.Quality); _owner.GetPlayer().CastSpell(_owner.GetPlayer(), speciesEntry.SummonSpellID != 0 ? speciesEntry.SummonSpellID : SharedConst.DefaultSummonBattlePetSpell); // TODO: set pet level, quality... update fields @@ -469,6 +481,7 @@ namespace Game.BattlePets { pet.DespawnOrUnsummon(); ownerPlayer.SetSummonedBattlePetGUID(ObjectGuid.Empty); + ownerPlayer.SetCurrentBattlePetBreedQuality((byte)BattlePetBreedQuality.Poor); } } @@ -519,7 +532,7 @@ namespace Game.BattlePets static Dictionary> _battlePetBreedStates = new(); static Dictionary> _battlePetSpeciesStates = new(); static MultiMap _availableBreedsPerSpecies = new(); - static Dictionary _defaultQualityPerSpecies = new(); + static Dictionary _defaultQualityPerSpecies = new(); public class BattlePet { diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 21db49347..6d20e4f74 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1416,6 +1416,21 @@ namespace Game.Entities SetStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Base, armor); } + void SelectWildBattlePetLevel() + { + if (IsWildBattlePet()) + { + byte wildBattlePetLevel = 1; + + var areaTable = CliDB.AreaTableStorage.LookupByKey(GetZoneId()); + if (areaTable != null) + if (areaTable.WildBattlePetLevelMin > 0) + wildBattlePetLevel = (byte)RandomHelper.URand(areaTable.WildBattlePetLevelMin, areaTable.WildBattlePetLevelMax); + + SetWildBattlePetLevel(wildBattlePetLevel); + } + } + float _GetHealthMod(CreatureEliteType Rank) { switch (Rank) // define rates for each elite rank @@ -3166,6 +3181,8 @@ namespace Game.Entities SetSpawnHealth(); + SelectWildBattlePetLevel(); + // checked at creature_template loading DefaultMovementType = (MovementGeneratorType)data.movementType; diff --git a/Source/Game/Entities/Object/Update/UpdateFields.cs b/Source/Game/Entities/Object/Update/UpdateFields.cs index c79afc90a..29bdcbcbc 100644 --- a/Source/Game/Entities/Object/Update/UpdateFields.cs +++ b/Source/Game/Entities/Object/Update/UpdateFields.cs @@ -1209,7 +1209,7 @@ namespace Game.Entities public UpdateField MinItemLevel = new(96, 105); public UpdateField MaxItemLevel = new(96, 106); public UpdateField AzeriteItemLevel = new(96, 107); - public UpdateField WildBattlePetLevel = new(96, 108); + public UpdateField WildBattlePetLevel = new(96, 108); public UpdateField BattlePetCompanionNameTimestamp = new(96, 109); public UpdateField InteractSpellID = new(96, 110); public UpdateField ScaleDuration = new(96, 111); @@ -1401,7 +1401,7 @@ namespace Game.Entities data.WriteUInt32(MinItemLevel); data.WriteUInt32(MaxItemLevel); data.WriteInt32(AzeriteItemLevel); - data.WriteInt32(WildBattlePetLevel); + data.WriteUInt32(WildBattlePetLevel); data.WriteUInt32(BattlePetCompanionNameTimestamp); data.WriteInt32(InteractSpellID); data.WriteInt32(ScaleDuration); @@ -1938,7 +1938,7 @@ namespace Game.Entities } if (changesMask[108]) { - data.WriteInt32(WildBattlePetLevel); + data.WriteUInt32(WildBattlePetLevel); } if (changesMask[109]) { diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 5a741c0c7..119b2059e 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -7399,6 +7399,7 @@ namespace Game.Entities public void SetArenaFaction(byte arenaFaction) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.ArenaFaction), arenaFaction); } public void ApplyModFakeInebriation(int mod, bool apply) { ApplyModUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.FakeInebriation), mod, apply); } public void SetVirtualPlayerRealm(uint virtualRealmAddress) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.VirtualPlayerRealm), virtualRealmAddress); } + public void SetCurrentBattlePetBreedQuality(byte battlePetBreedQuality) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.CurrentBattlePetBreedQuality), battlePetBreedQuality); } public void AddHeirloom(uint itemId, uint flags) { diff --git a/Source/Game/Entities/Unit/Unit.Pets.cs b/Source/Game/Entities/Unit/Unit.Pets.cs index e85bce05e..5a24c4b64 100644 --- a/Source/Game/Entities/Unit/Unit.Pets.cs +++ b/Source/Game/Entities/Unit/Unit.Pets.cs @@ -183,7 +183,14 @@ namespace Game.Entities SetCritterGUID(minion.GetGUID()); Player thisPlayer = ToPlayer(); if (thisPlayer != null) - minion.SetBattlePetCompanionGUID(thisPlayer.m_activePlayerData.SummonedBattlePetGUID); + { + var pet = thisPlayer.GetSession().GetBattlePetMgr().GetPet(thisPlayer.m_activePlayerData.SummonedBattlePetGUID); + if (pet != null) + { + minion.SetBattlePetCompanionGUID(thisPlayer.m_activePlayerData.SummonedBattlePetGUID); + minion.SetWildBattlePetLevel(pet.PacketInfo.Level); + } + } } // PvP, FFAPvP diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index 7c1b8e2f8..a00dd094a 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -767,6 +767,7 @@ namespace Game.Entities public bool IsTabardDesigner() { return HasNpcFlag(NPCFlags.TabardDesigner); } public bool IsAuctioner() { return HasNpcFlag(NPCFlags.Auctioneer); } public bool IsArmorer() { return HasNpcFlag(NPCFlags.Repair); } + public bool IsWildBattlePet() { return HasNpcFlag(NPCFlags.WildBattlePet); } public bool IsServiceProvider() { return HasNpcFlag(NPCFlags.Vendor | NPCFlags.Trainer | NPCFlags.FlightMaster | @@ -1831,6 +1832,10 @@ namespace Game.Entities public void SetCritterGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Critter), guid); } public ObjectGuid GetBattlePetCompanionGUID() { return m_unitData.BattlePetCompanionGUID; } public void SetBattlePetCompanionGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.BattlePetCompanionGUID), guid); } + + public void SetWildBattlePetLevel(uint wildBattlePetLevel) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.WildBattlePetLevel), wildBattlePetLevel); } + public uint GetWildBattlePetLevel() { return m_unitData.WildBattlePetLevel; } + public override ObjectGuid GetCharmerOrOwnerGUID() { return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID(); diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index bb8a3511a..142d35c8c 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -2973,13 +2973,9 @@ namespace Game.Spells if (!m_targets.HasDst()) return; - float x, y, z; float radius = effectInfo.CalcRadius(); for (byte i = 0; i < 15; ++i) - { - m_caster.GetRandomPoint(destTarget, radius, out x, out y, out z); - m_caster.CastSpell(new Position(x, y, z), 54522, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId)); - } + m_caster.CastSpell(m_caster.GetRandomPoint(destTarget, radius), 54522, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId)); break; } case 52173: // Coyote Spirit Despawn @@ -5446,7 +5442,7 @@ namespace Game.Spells uint speciesId = m_CastItem.GetModifier(ItemModifier.BattlePetSpeciesId); ushort breed = (ushort)(m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) & 0xFFFFFF); - byte quality = (byte)((m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) >> 24) & 0xFF); + BattlePetBreedQuality quality = (BattlePetBreedQuality)((m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) >> 24) & 0xFF); ushort level = (ushort)m_CastItem.GetModifier(ItemModifier.BattlePetLevel); uint creatureId = m_CastItem.GetModifier(ItemModifier.BattlePetDisplayId);