Core/Pets: Updated pet summoning for latest client version (5 Call Pet spells and stable size 200)

Port From (https://github.com/TrinityCore/TrinityCore/commit/950db60435e7e513633ff5b22ad7f0ed8b1147e4)
This commit is contained in:
hondacrx
2022-03-10 12:02:05 -05:00
parent a77e6865b2
commit c486b2a846
14 changed files with 448 additions and 218 deletions
+6 -4
View File
@@ -34,11 +34,13 @@ namespace Framework.Constants
public enum PetSaveMode
{
NotInSlot = -1, // for avoid conflict with stable size grow will use negative value
AsDeleted = -2, // not saved in fact
AsCurrent = 0, // in current slot (with player)
FirstStableSlot = 1,
LastStableSlot = SharedConst.MaxPetStables, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT
AsCurrent = -3, // in current slot (with player)
FirstActiveSlot = 0,
LastActiveSlot = FirstActiveSlot + SharedConst.MaxActivePets,
FirstStableSlot = 5,
LastStableSlot = FirstStableSlot + SharedConst.MaxPetStables, // last in DB stable slot index
NotInSlot = -1, // for avoid conflict with stable size grow will use negative value
}
public enum PetSpellState
+13 -1
View File
@@ -219,7 +219,9 @@ namespace Framework.Constants
/// <summary>
/// Pet Const
/// </summary>
public const int MaxPetStables = 4;
public const int MaxActivePets = 5;
public const int MaxPetStables = 200;
public const uint CallPetSpellId = 883;
public const float PetFollowDist = 1.0f;
public const float PetFollowAngle = MathF.PI;
public const int MaxSpellCharm = 4;
@@ -434,6 +436,16 @@ namespace Framework.Constants
{
return raceId < Race.Max && raceBits[(int)raceId] >= 0 && raceBits[(int)raceId] < 64 ? (1 << raceBits[(int)raceId]) : 0;
}
public static bool IsActivePetSlot(PetSaveMode slot)
{
return slot >= PetSaveMode.FirstActiveSlot && slot < PetSaveMode.LastActiveSlot;
}
public static bool IsStabledPetSlot(PetSaveMode slot)
{
return slot >= PetSaveMode.FirstStableSlot && slot < PetSaveMode.LastStableSlot;
}
}
public enum Locale
@@ -721,7 +721,6 @@ namespace Framework.Database
PrepareStatement(CharStatements.UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?");
PrepareStatement(CharStatements.UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?");
PrepareStatement(CharStatements.DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?");
PrepareStatement(CharStatements.DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND slot IN (?, ?)");
PrepareStatement(CharStatements.DEL_ALL_PET_SPELLS_BY_OWNER, "DELETE FROM pet_spell WHERE guid in (SELECT id FROM character_pet WHERE owner=?)");
PrepareStatement(CharStatements.UPD_PET_SPECS_BY_OWNER, "UPDATE character_pet SET specialization = 0 WHERE owner=?");
PrepareStatement(CharStatements.INS_PET, "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization) " +
@@ -1353,7 +1352,6 @@ namespace Framework.Database
UPD_CHAR_PET_NAME,
UPD_CHAR_PET_SLOT_BY_ID,
DEL_CHAR_PET_BY_ID,
DEL_CHAR_PET_BY_SLOT,
DEL_ALL_PET_SPELLS_BY_OWNER,
UPD_PET_SPECS_BY_OWNER,
INS_PET,
+69 -53
View File
@@ -96,13 +96,14 @@ namespace Game.Entities
}
}
public static Tuple<PetStable.PetInfo, PetSaveMode> GetLoadPetInfo(PetStable stable, uint petEntry, uint petnumber, bool current)
public static Tuple<PetStable.PetInfo, PetSaveMode> GetLoadPetInfo(PetStable stable, uint petEntry, uint petnumber, PetSaveMode? slot)
{
if (petnumber != 0)
{
// Known petnumber entry
if (stable.CurrentPet != null && stable.CurrentPet.PetNumber == petnumber)
return Tuple.Create(stable.CurrentPet, PetSaveMode.AsCurrent);
for (var activeSlot = 0; activeSlot < stable.ActivePets.Length; ++activeSlot)
if (stable.ActivePets[activeSlot] != null && stable.ActivePets[activeSlot].PetNumber == petnumber)
return Tuple.Create(stable.ActivePets[activeSlot], PetSaveMode.FirstActiveSlot + activeSlot);
for (var stableSlot = 0; stableSlot < stable.StabledPets.Length; ++stableSlot)
if (stable.StabledPets[stableSlot] != null && stable.StabledPets[stableSlot].PetNumber == petnumber)
@@ -112,17 +113,24 @@ namespace Game.Entities
if (pet.PetNumber == petnumber)
return Tuple.Create(pet, PetSaveMode.NotInSlot);
}
else if (current)
else if (slot.HasValue)
{
// Current pet (slot 0)
if (stable.CurrentPet != null)
return Tuple.Create(stable.CurrentPet, PetSaveMode.AsCurrent);
// Current pet
if (slot == PetSaveMode.AsCurrent)
if (stable.GetCurrentActivePetIndex().HasValue && stable.ActivePets[stable.GetCurrentActivePetIndex().Value] != null)
return Tuple.Create(stable.ActivePets[stable.GetCurrentActivePetIndex().Value], (PetSaveMode)stable.GetCurrentActivePetIndex());
if (slot >= PetSaveMode.FirstActiveSlot && slot < PetSaveMode.LastActiveSlot)
if (stable.ActivePets[(int)slot.Value] != null)
return Tuple.Create(stable.ActivePets[(int)slot.Value], slot.Value);
if (slot >= PetSaveMode.FirstStableSlot && slot < PetSaveMode.LastStableSlot)
if (stable.StabledPets[(int)slot.Value] != null)
return Tuple.Create(stable.StabledPets[(int)slot.Value], slot.Value);
}
else if (petEntry != 0)
{
// known petEntry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
if (stable.CurrentPet != null && stable.CurrentPet.CreatureId == petEntry)
return Tuple.Create(stable.CurrentPet, PetSaveMode.AsCurrent);
foreach (var pet in stable.UnslottedPets)
if (pet.CreatureId == petEntry)
@@ -131,8 +139,8 @@ namespace Game.Entities
else
{
// Any current or other non-stabled pet (for hunter "call pet")
if (stable.CurrentPet != null)
return Tuple.Create(stable.CurrentPet, PetSaveMode.AsCurrent);
if (stable.ActivePets[0] != null)
return Tuple.Create(stable.ActivePets[0], PetSaveMode.FirstActiveSlot);
if (!stable.UnslottedPets.Empty())
return Tuple.Create(stable.UnslottedPets.First(), PetSaveMode.NotInSlot);
@@ -141,22 +149,22 @@ namespace Game.Entities
return Tuple.Create<PetStable.PetInfo, PetSaveMode>(null, PetSaveMode.AsDeleted);
}
public bool LoadPetFromDB(Player owner, uint petEntry = 0, uint petnumber = 0, bool current = false)
public bool LoadPetFromDB(Player owner, uint petEntry = 0, uint petnumber = 0, bool current = false, PetSaveMode? forcedSlot = null)
{
m_loading = true;
PetStable petStable = owner.GetPetStable();
ulong ownerid = owner.GetGUID().GetCounter();
(PetStable.PetInfo petInfo, PetSaveMode slot) = GetLoadPetInfo(petStable, petEntry, petnumber, current);
if (petInfo == null)
(PetStable.PetInfo petInfo, PetSaveMode slot) = GetLoadPetInfo(petStable, petEntry, petnumber, forcedSlot);
if (petInfo == null || (slot >= PetSaveMode.FirstStableSlot && slot < PetSaveMode.LastStableSlot))
{
m_loading = false;
return false;
}
// Don't try to reload the current pet
if (petStable.CurrentPet != null && owner.GetPet() != null && petStable.CurrentPet.PetNumber == petInfo.PetNumber)
if (petStable.GetCurrentPet() != null && owner.GetPet() != null && petStable.GetCurrentPet().PetNumber == petInfo.PetNumber)
return false;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(petInfo.CreatedBySpellId, owner.GetMap().GetDifficultyID());
@@ -273,36 +281,28 @@ namespace Game.Entities
}
// set current pet as current
// 0=current
// 1..MAX_PET_STABLES in stable slot
// PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning))
// 0-4=current
// PET_SAVE_NOT_IN_SLOT(-1) = not stable slot (summoning))
if (slot == PetSaveMode.NotInSlot)
{
uint petInfoNumber = petInfo.PetNumber;
if (petStable.CurrentPet != null)
if (petStable.CurrentPetIndex != 0)
owner.RemovePet(null, PetSaveMode.NotInSlot);
var unslottedPetInfo = petStable.UnslottedPets.Find(unslottedPet => unslottedPet.PetNumber == petInfoNumber);
Cypher.Assert(petStable.CurrentPet == null);
Cypher.Assert(unslottedPetInfo != null);
var unslottedPetIndex = petStable.UnslottedPets.FindIndex(unslottedPet => unslottedPet.PetNumber == petInfoNumber);
Cypher.Assert(petStable.CurrentPetIndex == 0);
Cypher.Assert(unslottedPetIndex != -1);
petStable.CurrentPet = unslottedPetInfo;
petStable.UnslottedPets.Remove(unslottedPetInfo);
// old petInfo is no longer valid, refresh it
petInfo = petStable.CurrentPet;
petStable.SetCurrentUnslottedPetIndex((uint)unslottedPetIndex);
}
else if (PetSaveMode.FirstStableSlot <= slot && slot <= PetSaveMode.LastStableSlot)
else if (PetSaveMode.FirstActiveSlot <= slot && slot <= PetSaveMode.LastActiveSlot)
{
var index = Array.FindIndex(petStable.StabledPets, pet => pet?.PetNumber == petnumber);
var stabledPet = petStable.StabledPets[index];
Cypher.Assert(index != -1);
var activePetIndex = Array.FindIndex(petStable.ActivePets, pet => pet?.PetNumber == petnumber);
petStable.StabledPets[index] = petStable.CurrentPet;
petStable.CurrentPet = stabledPet;
Cypher.Assert(petStable.CurrentPetIndex == 0);
Cypher.Assert(activePetIndex != -1);
// old petInfo pointer is no longer valid, refresh it
petInfo = petStable.CurrentPet;
petStable.SetCurrentActivePetIndex((uint)activePetIndex);
}
// Send fake summon spell cast - this is needed for correct cooldown application for spells
@@ -437,8 +437,15 @@ namespace Game.Entities
// save auras before possibly removing them
_SaveAuras(trans);
if (mode == PetSaveMode.AsCurrent)
{
var activeSlot = owner.GetPetStable().GetCurrentActivePetIndex();
if (activeSlot.HasValue)
mode = (PetSaveMode)activeSlot;
}
// stable and not in slot saves
if (mode > PetSaveMode.AsCurrent)
if (mode < PetSaveMode.FirstActiveSlot || mode >= PetSaveMode.LastActiveSlot)
RemoveAllAuras();
_SaveSpells(trans);
@@ -446,7 +453,7 @@ namespace Game.Entities
DB.Characters.CommitTransaction(trans);
// current/stable/not_in_slot
if (mode >= PetSaveMode.AsCurrent)
if (mode != PetSaveMode.AsDeleted)
{
ulong ownerLowGUID = GetOwnerGUID().GetCounter();
trans = new SQLTransaction();
@@ -456,21 +463,11 @@ namespace Game.Entities
stmt.AddValue(0, GetCharmInfo().GetPetNumber());
trans.Append(stmt);
// prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
if (GetPetType() == PetType.Hunter && (mode == PetSaveMode.AsCurrent || mode == PetSaveMode.LastStableSlot))
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_PET_BY_SLOT);
stmt.AddValue(0, ownerLowGUID);
stmt.AddValue(1, (short)mode);
stmt.AddValue(2, (short)PetSaveMode.NotInSlot);
trans.Append(stmt);
}
// save pet
string actionBar = GenerateActionBarData();
Cypher.Assert(owner.GetPetStable().CurrentPet != null && owner.GetPetStable().CurrentPet.PetNumber == GetCharmInfo().GetPetNumber());
FillPetInfo(owner.GetPetStable().CurrentPet);
Cypher.Assert(owner.GetPetStable().GetCurrentPet() != null && owner.GetPetStable().GetCurrentPet().PetNumber == GetCharmInfo().GetPetNumber());
FillPetInfo(owner.GetPetStable().GetCurrentPet());
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_PET);
stmt.AddValue(0, GetCharmInfo().GetPetNumber());
@@ -480,7 +477,7 @@ namespace Game.Entities
stmt.AddValue(4, GetLevel());
stmt.AddValue(5, m_unitData.PetExperience);
stmt.AddValue(6, (byte)GetReactState());
stmt.AddValue(7, (byte)mode);
stmt.AddValue(7, (owner.GetPetStable().GetCurrentActivePetIndex().HasValue ? (short)owner.GetPetStable().GetCurrentActivePetIndex().Value : (short)PetSaveMode.NotInSlot));
stmt.AddValue(8, GetName());
stmt.AddValue(9, HasPetFlag(UnitPetFlags.CanBeRenamed) ? 0 : 1);
stmt.AddValue(10, curhealth);
@@ -1676,6 +1673,8 @@ namespace Game.Entities
public class PetStable
{
static uint UnslottedPetIndexMask = 0x80000000;
public class PetInfo
{
public string Name;
@@ -1695,14 +1694,31 @@ namespace Game.Entities
public bool WasRenamed;
}
public PetInfo CurrentPet; // PET_SAVE_AS_CURRENT
public uint? CurrentPetIndex; // index into ActivePets or UnslottedPets if highest bit is set
public PetInfo[] ActivePets = new PetInfo[SharedConst.MaxActivePets]; // PET_SAVE_FIRST_ACTIVE_SLOT - PET_SAVE_LAST_ACTIVE_SLOT
public PetInfo[] StabledPets = new PetInfo[SharedConst.MaxPetStables]; // PET_SAVE_FIRST_STABLE_SLOT - PET_SAVE_LAST_STABLE_SLOT
public List<PetInfo> UnslottedPets = new(); // PET_SAVE_NOT_IN_SLOT
public PetInfo GetUnslottedHunterPet()
public PetInfo GetCurrentPet()
{
return UnslottedPets.Count == 1 && UnslottedPets[0].Type == PetType.Hunter ? UnslottedPets[0] : null;
if (!CurrentPetIndex.HasValue)
return null;
uint? activePetIndex = GetCurrentActivePetIndex();
if (activePetIndex.HasValue)
return ActivePets[activePetIndex.Value] != null ? ActivePets[activePetIndex.Value] : null;
uint? unslottedPetIndex = GetCurrentUnslottedPetIndex();
if (unslottedPetIndex.HasValue)
return unslottedPetIndex < UnslottedPets.Count ? UnslottedPets[(int)unslottedPetIndex.Value] : null;
return null;
}
public uint? GetCurrentActivePetIndex() { return CurrentPetIndex.HasValue && ((CurrentPetIndex & UnslottedPetIndexMask) == 0) ? CurrentPetIndex : null; }
public void SetCurrentActivePetIndex(uint index) { CurrentPetIndex = index; }
uint? GetCurrentUnslottedPetIndex() { return CurrentPetIndex.HasValue && ((CurrentPetIndex & UnslottedPetIndexMask) != 0) ? (CurrentPetIndex & ~UnslottedPetIndexMask) : null; }
public void SetCurrentUnslottedPetIndex(uint index) { CurrentPetIndex = index | UnslottedPetIndexMask; }
}
public enum ActiveStates
+10 -8
View File
@@ -1538,7 +1538,7 @@ namespace Game.Entities
m_bgData.taxiPath[1] = result.Read<uint>(8);
m_bgData.mountSpell = result.Read<uint>(9);
}
void _LoadPetStable(SQLResult result)
void _LoadPetStable(uint summonedPetNumber, SQLResult result)
{
if (result.IsEmpty())
return;
@@ -1568,15 +1568,18 @@ namespace Game.Entities
petInfo.CreatedBySpellId = result.Read<uint>(13);
petInfo.Type = (PetType)result.Read<byte>(14);
petInfo.SpecializationId = result.Read<ushort>(15);
if (slot == PetSaveMode.AsCurrent)
m_petStable.CurrentPet = petInfo;
else if (slot >= PetSaveMode.FirstStableSlot && slot <= PetSaveMode.LastStableSlot)
m_petStable.StabledPets[(int)slot - 1] = petInfo;
if (slot >= PetSaveMode.FirstActiveSlot && slot < PetSaveMode.LastActiveSlot)
m_petStable.ActivePets[(int)slot] = petInfo;
else if (slot >= PetSaveMode.FirstStableSlot && slot < PetSaveMode.LastStableSlot)
m_petStable.StabledPets[slot - PetSaveMode.FirstStableSlot] = petInfo;
else if (slot == PetSaveMode.NotInSlot)
m_petStable.UnslottedPets.Add(petInfo);
} while (result.NextRow());
}
if (Pet.GetLoadPetInfo(m_petStable, 0, summonedPetNumber, null).Item1 != null)
m_temporaryUnsummonedPetNumber = summonedPetNumber;
}
@@ -3080,8 +3083,7 @@ namespace Game.Entities
m_taxi.LoadTaxiMask(taximask); // must be before InitTaxiNodesForLevel
_LoadPetStable(holder.GetResult(PlayerLoginQueryLoad.PetSlots));
m_temporaryUnsummonedPetNumber = summonedPetNumber;
_LoadPetStable(summonedPetNumber, holder.GetResult(PlayerLoginQueryLoad.PetSlots));
// Honor system
// Update Honor kills data
@@ -3590,7 +3592,7 @@ namespace Game.Entities
stmt.AddValue(index++, (ushort)m_ExtraFlags);
PetStable petStable = GetPetStable();
if (petStable != null)
stmt.AddValue(index++, petStable.CurrentPet != null && petStable.CurrentPet.Health > 0 ? petStable.CurrentPet.PetNumber : 0); // summonedPetNumber
stmt.AddValue(index++, petStable.GetCurrentPet() != null && petStable.GetCurrentPet().Health > 0 ? petStable.GetCurrentPet().PetNumber : 0); // summonedPetNumber
else
stmt.AddValue(index++, 0); // summonedPetNumber
stmt.AddValue(index++, (ushort)atLoginFlags);
+39 -67
View File
@@ -4701,12 +4701,19 @@ namespace Game.Entities
return null;
}
public Pet SummonPet(uint entry, float x, float y, float z, float ang, PetType petType, uint duration)
public Pet SummonPet(uint entry, PetSaveMode? slot, float x, float y, float z, float ang, uint duration)
{
return SummonPet(entry, slot, x, y, z, ang, duration, out _);
}
public Pet SummonPet(uint entry, PetSaveMode? slot, float x, float y, float z, float ang, uint duration, out bool isNew)
{
isNew = false;
PetStable petStable = GetOrInitPetStable();
Pet pet = new(this, petType);
if (petType == PetType.Summon && pet.LoadPetFromDB(this, entry, 0, false))
Pet pet = new(this, PetType.Summon);
if (pet.LoadPetFromDB(this, entry, 0, false, slot))
{
if (duration > 0)
pet.SetDuration(duration);
@@ -4718,6 +4725,8 @@ namespace Game.Entities
if (entry == 0)
return null;
// only SUMMON_PET are handled here
pet.Relocate(x, y, z, ang);
if (!pet.IsPositionValid())
{
@@ -4734,7 +4743,7 @@ namespace Game.Entities
return null;
}
if (petType == PetType.Summon && petStable.CurrentPet != null)
if (petStable.GetCurrentPet() != null)
RemovePet(null, PetSaveMode.NotInSlot);
PhasingHandler.InheritPhaseShift(pet, this);
@@ -4747,44 +4756,34 @@ namespace Game.Entities
SetMinion(pet, true);
Cypher.Assert(petStable.CurrentPet == null && (petType != PetType.Hunter || petStable.GetUnslottedHunterPet() == null));
petStable.CurrentPet = new();
pet.FillPetInfo(petStable.CurrentPet);
switch (petType)
{
case PetType.Summon:
// this enables pet details window (Shift+P)
pet.GetCharmInfo().SetPetNumber(petNumber, true);
pet.SetClass(Class.Mage);
pet.SetPetExperience(0);
pet.SetPetNextLevelExperience(1000);
pet.SetFullHealth();
pet.SetFullPower(PowerType.Mana);
pet.SetPetNameTimestamp((uint)GameTime.GetGameTime());
break;
default:
break;
}
// this enables pet details window (Shift+P)
pet.GetCharmInfo().SetPetNumber(petNumber, true);
pet.SetClass(Class.Mage);
pet.SetPetExperience(0);
pet.SetPetNextLevelExperience(1000);
pet.SetFullHealth();
pet.SetFullPower(PowerType.Mana);
pet.SetPetNameTimestamp((uint)GameTime.GetGameTime());
map.AddToMap(pet.ToCreature());
switch (petType)
{
case PetType.Summon:
pet.InitPetCreateSpells();
pet.SavePetToDB(PetSaveMode.AsCurrent);
PetSpellInitialize();
break;
default:
break;
}
Cypher.Assert(petStable.CurrentPetIndex == 0);
petStable.SetCurrentUnslottedPetIndex((uint)petStable.UnslottedPets.Count);
PetStable.PetInfo petInfo = new();
pet.FillPetInfo(petInfo);
petStable.UnslottedPets.Add(petInfo);
pet.InitPetCreateSpells();
pet.SavePetToDB(PetSaveMode.AsCurrent);
PetSpellInitialize();
if (duration > 0)
pet.SetDuration(duration);
//ObjectAccessor.UpdateObjectVisibility(pet);
isNew = true;
return pet;
}
@@ -4829,49 +4828,22 @@ namespace Game.Entities
if (pet == null)
{
if (mode == PetSaveMode.NotInSlot && m_petStable?.CurrentPet != null)
{
// Handle removing pet while it is in "temporarily unsummoned" state, for example on mount
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_PET_SLOT_BY_ID);
stmt.AddValue(0, (short)PetSaveMode.NotInSlot);
stmt.AddValue(1, GetGUID().GetCounter());
stmt.AddValue(2, m_petStable.CurrentPet.PetNumber);
DB.Characters.Execute(stmt);
m_petStable.UnslottedPets.Add(m_petStable.CurrentPet);
m_petStable.CurrentPet = null;
}
// Handle removing pet while it is in "temporarily unsummoned" state, for example on mount
if (mode == PetSaveMode.NotInSlot && m_petStable != null && m_petStable.CurrentPetIndex.HasValue)
m_petStable.CurrentPetIndex = null;
return;
}
pet.CombatStop();
if (returnreagent)
{
switch (pet.GetEntry())
{
//warlock pets except imp are removed(?) when logging out
case 1860:
case 1863:
case 417:
case 17252:
mode = PetSaveMode.NotInSlot;
break;
}
}
// only if current pet in slot
pet.SavePetToDB(mode);
Cypher.Assert(m_petStable.CurrentPet != null && m_petStable.CurrentPet.PetNumber == pet.GetCharmInfo().GetPetNumber());
if (mode == PetSaveMode.NotInSlot)
{
m_petStable.UnslottedPets.Add(m_petStable.CurrentPet);
m_petStable.CurrentPet = null;
}
else if (mode == PetSaveMode.AsDeleted)
m_petStable.CurrentPet = null;
PetStable.PetInfo currentPet = m_petStable.GetCurrentPet();
Cypher.Assert(currentPet != null && currentPet.PetNumber == pet.GetCharmInfo().GetPetNumber());
if (mode == PetSaveMode.NotInSlot || mode == PetSaveMode.AsDeleted)
m_petStable.CurrentPetIndex = null;
// else if (stable slots) handled in opcode handlers due to required swaps
// else (current pet) doesnt need to do anything
+8 -3
View File
@@ -761,7 +761,9 @@ namespace Game.Entities
{
Player player = ToPlayer();
PetStable petStable = player.GetOrInitPetStable();
if (petStable.CurrentPet != null || petStable.GetUnslottedHunterPet() != null)
var freeActiveSlot = Array.FindIndex(petStable.ActivePets, petInfo => petInfo == null);
if (freeActiveSlot == -1)
return false;
pet.SetCreatorGUID(GetGUID());
@@ -784,8 +786,11 @@ namespace Game.Entities
pet.InitPetCreateSpells();
pet.SetFullHealth();
petStable.CurrentPet = new();
pet.FillPetInfo(petStable.CurrentPet);
petStable.SetCurrentActivePetIndex((uint)freeActiveSlot);
PetStable.PetInfo petInfo = new();
pet.FillPetInfo(petInfo);
petStable.ActivePets[freeActiveSlot] = petInfo;
return true;
}
+4
View File
@@ -792,6 +792,10 @@ namespace Game
pCurrChar.SetGuildLevel(0);
}
// Send stable contents to display icons on Call Pet spells
if (pCurrChar.HasSpell(SharedConst.CallPetSpellId))
SendStablePet(ObjectGuid.Empty);
pCurrChar.GetSession().GetBattlePetMgr().SendJournalLockStatus();
pCurrChar.SendInitialPacketsBeforeAddToMap();
+166 -38
View File
@@ -324,7 +324,7 @@ namespace Game
GetPlayer().PlayerTalkClass.SendCloseGossip();
}
[WorldPacketHandler(ClientOpcodes.RequestStabledPets)]
[WorldPacketHandler(ClientOpcodes.RequestStabledPets, Processing = PacketProcessing.Inplace)]
void HandleRequestStabledPets(RequestStabledPets packet)
{
if (!CheckStableMaster(packet.StableMaster))
@@ -353,58 +353,40 @@ namespace Game
return;
}
uint petSlot = 0;
if (petStable.CurrentPet != null)
for (uint petSlot = 0; petSlot < petStable.ActivePets.Length; ++petSlot)
{
PetStable.PetInfo pet = petStable.CurrentPet;
if (petStable.ActivePets[petSlot] == null)
continue;
PetStable.PetInfo pet = petStable.ActivePets[petSlot];
PetStableInfo stableEntry;
stableEntry.PetSlot = petSlot;
stableEntry.PetSlot = petSlot + (int)PetSaveMode.FirstActiveSlot;
stableEntry.PetNumber = pet.PetNumber;
stableEntry.CreatureID = pet.CreatureId;
stableEntry.DisplayID = pet.DisplayId;
stableEntry.ExperienceLevel = pet.Level;
stableEntry.PetFlags = PetStableinfo.Active;
stableEntry.PetName = pet.Name;
++petSlot;
packet.Pets.Add(stableEntry);
}
else
for (uint petSlot = 0; petSlot < petStable.StabledPets.Length; ++petSlot)
{
PetStable.PetInfo pet = petStable.GetUnslottedHunterPet();
if (pet != null)
{
PetStableInfo stableEntry;
stableEntry.PetSlot = petSlot;
stableEntry.PetNumber = pet.PetNumber;
stableEntry.CreatureID = pet.CreatureId;
stableEntry.DisplayID = pet.DisplayId;
stableEntry.ExperienceLevel = pet.Level;
stableEntry.PetFlags = PetStableinfo.Active;
stableEntry.PetName = pet.Name;
if (petStable.StabledPets[petSlot] == null)
continue;
++petSlot;
packet.Pets.Add(stableEntry);
}
}
PetStable.PetInfo pet = petStable.StabledPets[petSlot];
PetStableInfo stableEntry;
stableEntry.PetSlot = petSlot + (int)PetSaveMode.FirstStableSlot;
stableEntry.PetNumber = pet.PetNumber;
stableEntry.CreatureID = pet.CreatureId;
stableEntry.DisplayID = pet.DisplayId;
stableEntry.ExperienceLevel = pet.Level;
stableEntry.PetFlags = PetStableinfo.Inactive;
stableEntry.PetName = pet.Name;
foreach (var pet in petStable.StabledPets)
{
if (pet != null)
{
PetStableInfo stableEntry;
stableEntry.PetSlot = petSlot;
stableEntry.PetNumber = pet.PetNumber;
stableEntry.CreatureID = pet.CreatureId;
stableEntry.DisplayID = pet.DisplayId;
stableEntry.ExperienceLevel = pet.Level;
stableEntry.PetFlags = PetStableinfo.Inactive;
stableEntry.PetName = pet.Name;
++petSlot;
packet.Pets.Add(stableEntry);
}
packet.Pets.Add(stableEntry);
}
SendPacket(packet);
@@ -417,6 +399,152 @@ namespace Game
SendPacket(petStableResult);
}
[WorldPacketHandler(ClientOpcodes.SetPetSlot)]
void HandleSetPetSlot(SetPetSlot setPetSlot)
{
if (!CheckStableMaster(setPetSlot.StableMaster) || setPetSlot.DestSlot >= (byte)PetSaveMode.LastStableSlot)
{
SendPetStableResult(StableResult.InternalError);
return;
}
GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting);
PetStable petStable = GetPlayer().GetPetStable();
if (petStable == null)
{
SendPetStableResult(StableResult.InternalError);
return;
}
(PetStable.PetInfo srcPet, PetSaveMode srcPetSlot) = Pet.GetLoadPetInfo(petStable, 0, setPetSlot.PetNumber, null);
PetSaveMode dstPetSlot = (PetSaveMode)setPetSlot.DestSlot;
PetStable.PetInfo dstPet = Pet.GetLoadPetInfo(petStable, 0, 0, dstPetSlot).Item1;
if (srcPet == null || srcPet.Type != PetType.Hunter)
{
SendPetStableResult(StableResult.InternalError);
return;
}
if (dstPet != null && dstPet.Type != PetType.Hunter)
{
SendPetStableResult(StableResult.InternalError);
return;
}
PetStable.PetInfo src = null;
PetStable.PetInfo dst = null;
PetSaveMode? newActivePetIndex = null;
if (SharedConst.IsActivePetSlot(srcPetSlot) && SharedConst.IsActivePetSlot(dstPetSlot))
{
// active<.active: only swap ActivePets and CurrentPetIndex (do not despawn pets)
src = petStable.ActivePets[srcPetSlot - PetSaveMode.FirstActiveSlot];
dst = petStable.ActivePets[dstPetSlot - PetSaveMode.FirstActiveSlot];
if (petStable.GetCurrentActivePetIndex().Value == (uint)srcPetSlot)
newActivePetIndex = dstPetSlot;
else if (petStable.GetCurrentActivePetIndex().Value == (uint)dstPetSlot)
newActivePetIndex = srcPetSlot;
}
else if (SharedConst.IsStabledPetSlot(srcPetSlot) && SharedConst.IsStabledPetSlot(dstPetSlot))
{
// stabled<.stabled: only swap StabledPets
src = petStable.StabledPets[srcPetSlot - PetSaveMode.FirstStableSlot];
dst = petStable.StabledPets[dstPetSlot - PetSaveMode.FirstStableSlot];
}
else if (SharedConst.IsActivePetSlot(srcPetSlot) && SharedConst.IsStabledPetSlot(dstPetSlot))
{
// active<.stabled: swap petStable contents and despawn active pet if it is involved in swap
if (petStable.CurrentPetIndex.Value == (uint)srcPetSlot)
{
Pet oldPet = _player.GetPet();
if (oldPet != null && !oldPet.IsAlive())
{
SendPetStableResult(StableResult.InternalError);
return;
}
_player.RemovePet(oldPet, PetSaveMode.NotInSlot);
}
if (dstPet != null)
{
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(dstPet.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets()))
{
SendPetStableResult(StableResult.CantControlExotic);
return;
}
}
src = petStable.ActivePets[srcPetSlot - PetSaveMode.FirstActiveSlot];
dst = petStable.StabledPets[dstPetSlot - PetSaveMode.FirstStableSlot];
}
else if (SharedConst.IsStabledPetSlot(srcPetSlot) && SharedConst.IsActivePetSlot(dstPetSlot))
{
// stabled<.active: swap petStable contents and despawn active pet if it is involved in swap
if (petStable.CurrentPetIndex.Value == (uint)dstPetSlot)
{
Pet oldPet = _player.GetPet();
if (oldPet != null && !oldPet.IsAlive())
{
SendPetStableResult(StableResult.InternalError);
return;
}
_player.RemovePet(oldPet, PetSaveMode.NotInSlot);
}
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(srcPet.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets()))
{
SendPetStableResult(StableResult.CantControlExotic);
return;
}
src = petStable.StabledPets[srcPetSlot - PetSaveMode.FirstStableSlot];
dst = petStable.ActivePets[dstPetSlot - PetSaveMode.FirstActiveSlot];
}
SQLTransaction trans = new();
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_PET_SLOT_BY_ID);
stmt.AddValue(0, (short)dstPetSlot);
stmt.AddValue(1, _player.GetGUID().GetCounter());
stmt.AddValue(2, srcPet.PetNumber);
trans.Append(stmt);
if (dstPet != null)
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_PET_SLOT_BY_ID);
stmt.AddValue(0, (short)srcPetSlot);
stmt.AddValue(1, _player.GetGUID().GetCounter());
stmt.AddValue(2, dstPet.PetNumber);
trans.Append(stmt);
}
AddTransactionCallback(DB.Characters.AsyncCommitTransaction(trans)).AfterComplete(success =>
{
var currentPlayerGuid = _player.GetGUID();
if (_player && _player.GetGUID() == currentPlayerGuid)
{
if (success)
{
Extensions.Swap(ref src, ref dst);
if (newActivePetIndex.HasValue)
GetPlayer().GetPetStable().SetCurrentActivePetIndex((uint)newActivePetIndex.Value);
SendPetStableResult(StableResult.StableSuccess);
}
else
{
SendPetStableResult(StableResult.InternalError);
}
}
});
}
[WorldPacketHandler(ClientOpcodes.RepairItem, Processing = PacketProcessing.Inplace)]
void HandleRepairItem(RepairItem packet)
{
+3 -3
View File
@@ -541,7 +541,7 @@ namespace Game
// check it!
if (!pet || !pet.IsPet() || pet.ToPet().GetPetType() != PetType.Hunter || !pet.HasPetFlag(UnitPetFlags.CanBeRenamed) ||
pet.GetOwnerGUID() != _player.GetGUID() || pet.GetCharmInfo() == null ||
petStable == null || petStable.CurrentPet == null || petStable.CurrentPet.PetNumber != pet.GetCharmInfo().GetPetNumber())
petStable == null || petStable.GetCurrentPet() == null || petStable.GetCurrentPet().PetNumber != pet.GetCharmInfo().GetPetNumber())
return;
PetNameInvalidReason res = ObjectManager.CheckPetName(name);
@@ -561,8 +561,8 @@ namespace Game
pet.SetGroupUpdateFlag(GroupUpdatePetFlags.Name);
pet.RemovePetFlag(UnitPetFlags.CanBeRenamed);
petStable.CurrentPet.Name = name;
petStable.CurrentPet.WasRenamed = true;
petStable.GetCurrentPet().Name = name;
petStable.GetCurrentPet().WasRenamed = true;
PreparedStatement stmt;
SQLTransaction trans = new();
@@ -292,6 +292,22 @@ namespace Game.Networking.Packets
public ObjectGuid StableMaster;
}
class SetPetSlot : ClientPacket
{
public SetPetSlot(WorldPacket packet) : base(packet) { }
public override void Read()
{
PetNumber = _worldPacket.ReadUInt32();
DestSlot = _worldPacket.ReadUInt8();
StableMaster = _worldPacket.ReadPackedGuid();
}
public ObjectGuid StableMaster;
public uint PetNumber;
public byte DestSlot;
}
//Structs
public struct TreasureItem
{
+38 -6
View File
@@ -5148,13 +5148,20 @@ namespace Game.Spells
}
case SpellEffectName.ResurrectPet:
{
if (unitCaster == null)
Player playerCaster = m_caster.ToPlayer();
if (playerCaster == null || playerCaster.GetPetStable() == null)
return SpellCastResult.BadTargets;
Creature pet = unitCaster.GetGuardianPet();
Pet pet = playerCaster.GetPet();
if (pet != null && pet.IsAlive())
return SpellCastResult.AlreadyHaveSummon;
PetStable petStable = playerCaster.GetPetStable();
var deadPetInfo = petStable.ActivePets.FirstOrDefault(petInfo => petInfo?.Health == 0);
if (deadPetInfo == null)
return SpellCastResult.BadTargets;
break;
}
// This is generic summon effect
@@ -5221,17 +5228,27 @@ namespace Game.Spells
Player playerCaster = unitCaster.ToPlayer();
if (playerCaster != null && playerCaster.GetPetStable() != null)
{
var info = Pet.GetLoadPetInfo(playerCaster.GetPetStable(), (uint)spellEffectInfo.MiscValue, 0, false);
if (info.Item1 != null)
PetSaveMode? petSlot = null;
if (spellEffectInfo.MiscValue == 0)
{
if (info.Item1.Type == PetType.Hunter)
petSlot = (PetSaveMode)spellEffectInfo.CalcValue();
// No pet can be summoned if any pet is dead
foreach (var activePet in playerCaster.GetPetStable().ActivePets)
{
if (info.Item1.Health == 0)
if (activePet?.Health == 0)
{
playerCaster.SendTameFailure(PetTameResult.Dead);
return SpellCastResult.DontReport;
}
}
}
var info = Pet.GetLoadPetInfo(playerCaster.GetPetStable(), (uint)spellEffectInfo.MiscValue, 0, petSlot);
if (info.Item1 != null)
{
if (info.Item1.Type == PetType.Hunter)
{
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(info.Item1.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(playerCaster.CanTameExoticPets()))
{
@@ -5253,6 +5270,21 @@ namespace Game.Spells
}
break;
}
case SpellEffectName.DismissPet:
{
Player playerCaster = m_caster.ToPlayer();
if (playerCaster == null)
return SpellCastResult.BadTargets;
Pet pet = playerCaster.GetPet();
if (pet == null)
return SpellCastResult.NoPet;
if (!pet.IsAlive())
return SpellCastResult.TargetsDead;
break;
}
case SpellEffectName.SummonPlayer:
{
if (!m_caster.IsTypeId(TypeId.Player))
+64 -31
View File
@@ -2298,27 +2298,34 @@ namespace Game.Spells
return;
}
PetSaveMode? petSlot = null;
if (petentry == 0)
petSlot = (PetSaveMode)damage;
float x, y, z;
owner.GetClosePoint(out x, out y, out z, owner.GetCombatReach());
Pet pet = owner.SummonPet(petentry, x, y, z, owner.Orientation, PetType.Summon, 0);
if (!pet)
Pet pet = owner.SummonPet(petentry, petSlot, x, y, z, owner.Orientation, 0, out bool isNew);
if (pet == null)
return;
if (m_caster.IsTypeId(TypeId.Unit))
if (isNew)
{
if (m_caster.ToCreature().IsTotem())
pet.SetReactState(ReactStates.Aggressive);
else
pet.SetReactState(ReactStates.Defensive);
if (m_caster.IsCreature())
{
if (m_caster.ToCreature().IsTotem())
pet.SetReactState(ReactStates.Aggressive);
else
pet.SetReactState(ReactStates.Defensive);
}
pet.SetCreatedBySpell(m_spellInfo.Id);
// generate new name for summon pet
string new_name = Global.ObjectMgr.GeneratePetName(petentry);
if (!string.IsNullOrEmpty(new_name))
pet.SetName(new_name);
}
pet.SetCreatedBySpell(m_spellInfo.Id);
// generate new name for summon pet
string new_name = Global.ObjectMgr.GeneratePetName(petentry);
if (!string.IsNullOrEmpty(new_name))
pet.SetName(new_name);
ExecuteLogEffectSummonObject(effectInfo.Effect, pet);
}
@@ -4012,42 +4019,68 @@ namespace Game.Spells
}
[SpellEffectHandler(SpellEffectName.ResurrectPet)]
void EffectSummonDeadPet()
void EffectResurrectPet()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (damage < 0)
return;
Player player = m_caster.ToPlayer();
if (player == null)
return;
Pet pet = player.GetPet();
if (pet != null && pet.IsAlive())
return;
// Maybe player dismissed dead pet or pet despawned?
bool hadPet = true;
if (damage < 0)
return;
float x, y, z;
player.GetPosition(out x, out y, out z);
if (pet == null)
if (player.GetPet() == null)
{
player.SummonPet(0, x, y, z, player.Orientation, PetType.Summon, 0);
pet = player.GetPet();
PetStable petStable = player.GetPetStable();
var deadPetIndex = Array.FindIndex(petStable.ActivePets, petInfo => petInfo?.Health == 0);
PetSaveMode slot = (PetSaveMode)deadPetIndex;
player.SummonPet(0, slot, 0f, 0f, 0f, 0f, 0);
hadPet = false;
}
if (pet == null)
// TODO: Better to fail Hunter's "Revive Pet" at cast instead of here when casting ends
Pet pet = player.GetPet(); // Attempt to get current pet
if (pet == null || pet.IsAlive())
return;
player.GetMap().CreatureRelocation(pet, x, y, z, player.GetOrientation());
// If player did have a pet before reviving, teleport it
if (hadPet)
{
// Reposition the pet's corpse before reviving so as not to grab aggro
// We can use a different, more accurate version of GetClosePoint() since we have a pet
// Will be used later to reposition the pet if we have one
player.GetClosePoint(out float x, out float y, out float z, pet.GetCombatReach(), SharedConst.PetFollowDist, pet.GetFollowAngle());
pet.NearTeleportTo(x, y, z, player.GetOrientation());
pet.Relocate(x, y, z, player.GetOrientation()); // This is needed so SaveStayPosition() will get the proper coords.
}
pet.SetDynamicFlags(UnitDynFlags.HideModel);
pet.SetDynamicFlags(UnitDynFlags.None);
pet.RemoveUnitFlag(UnitFlags.Skinnable);
pet.SetDeathState(DeathState.Alive);
pet.ClearUnitState(UnitState.AllErasable);
pet.SetHealth(pet.CountPctFromMaxHealth(damage));
pet.InitializeAI();
player.PetSpellInitialize();
// Reset things for when the AI to takes over
CharmInfo ci = pet.GetCharmInfo();
if (ci != null)
{
// In case the pet was at stay, we don't want it running back
ci.SaveStayPosition();
ci.SetIsAtStay(ci.HasCommandState(CommandStates.Stay));
ci.SetIsFollowing(false);
ci.SetIsCommandAttack(false);
ci.SetIsCommandFollow(false);
ci.SetIsReturning(false);
}
pet.SavePetToDB(PetSaveMode.AsCurrent);
}
+12 -2
View File
@@ -405,6 +405,8 @@ namespace Scripts.Spells.Hunter
[Script]
class spell_hun_tame_beast : SpellScript
{
static uint[] CallPetSpellIds = { 883, 83242, 83243, 83244, 83245, };
SpellCastResult CheckCast()
{
Player caster = GetCaster().ToPlayer();
@@ -427,10 +429,18 @@ namespace Scripts.Spells.Hunter
PetStable petStable = caster.GetPetStable();
if (petStable != null)
{
if (petStable.CurrentPet != null)
if (petStable.CurrentPetIndex.HasValue)
return SpellCastResult.AlreadyHaveSummon;
if (petStable.GetUnslottedHunterPet() != null)
var freeSlotIndex = Array.FindIndex(petStable.ActivePets, petInfo => petInfo == null);
if (freeSlotIndex == -1)
{
caster.SendTameFailure(PetTameResult.TooMany);
return SpellCastResult.DontReport;
}
// Check for known Call Pet X spells
if (!caster.HasSpell(CallPetSpellIds[freeSlotIndex]))
{
caster.SendTameFailure(PetTameResult.TooMany);
return SpellCastResult.DontReport;