/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * 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 3 of the License, or * 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 . * */ #include "StdAfx.h" #define GROWL_RANK_1 2649 #define GROWL_RANK_2 14916 #define WATER_ELEMENTAL 510 void Pet::CreateAsSummon(uint32 entry, CreatureInfo *ci, Creature* created_from_creature, Unit *owner, SpellEntry* created_by_spell, uint32 type, uint32 expiretime) { SetIsPet(true); //std::string myname = sWorld.GenerateName(); if(!ci) return; m_Owner = static_cast(owner); m_OwnerGuid = m_Owner->GetGUID(); creature_info = ci; myFamily = sCreatureFamilyStore.LookupEntry(creature_info->Family); m_name = sCreatureFamilyStore.LookupString(myFamily->name); // Create ourself Create(m_name.c_str(), owner->GetMapId(), owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation()); SetUInt32Value(OBJECT_FIELD_ENTRY, entry); SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // better set this one // Fields common to both lock summons and pets if(type & 0x2 && created_from_creature != NULL) SetUInt32Value(UNIT_FIELD_LEVEL, created_from_creature->getLevel()); else SetUInt32Value(UNIT_FIELD_LEVEL,owner->GetUInt32Value(UNIT_FIELD_LEVEL)); SetUInt32Value(UNIT_FIELD_DISPLAYID, ci->DisplayID); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, ci->DisplayID); SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID()); SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID()); if(type & 0x1 && created_by_spell != NULL) SetUInt64Value(UNIT_CREATED_BY_SPELL, created_by_spell->Id); if(type & 0x1 || created_from_creature == NULL) { Summon = true; SetUInt32Value(UNIT_FIELD_BYTES_0, 2048 | (0 << 24)); SetUInt32Value(UNIT_FIELD_FLAGS, 8); SetUInt32Value(UNIT_FIELD_BASEATTACKTIME, 2000); SetUInt32Value(UNIT_FIELD_BASEATTACKTIME_01, 2000); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 0.5f); SetFloatValue(UNIT_FIELD_COMBATREACH, 0.75f); SetUInt32Value(UNIT_FIELD_BYTES_2, (0x01 | (0x28 << 8) | (0x2 << 24))); SetUInt32Value(UNIT_FIELD_PETNUMBER, GetGUIDLow()); SetPowerType(POWER_TYPE_MANA); if(entry == WATER_ELEMENTAL) m_name = "SBAL & DrMadison"; else m_name = sWorld.GenerateName(); } else { SetUInt32Value(UNIT_FIELD_BYTES_0, 2048 | (0 << 24)); SetUInt32Value(UNIT_FIELD_BASEATTACKTIME, created_from_creature->GetUInt32Value(UNIT_FIELD_BASEATTACKTIME)); SetUInt32Value(UNIT_FIELD_BASEATTACKTIME_01, created_from_creature->GetUInt32Value(UNIT_FIELD_BASEATTACKTIME_01)); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, created_from_creature->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS)); SetFloatValue(UNIT_FIELD_COMBATREACH, created_from_creature->GetFloatValue(UNIT_FIELD_COMBATREACH)); // These need to be checked. SetUInt32Value(UNIT_FIELD_FLAGS, 0x00080008); SetUInt32Value(UNIT_FIELD_POWER5, PET_HAPPINESS_UPDATE_VALUE / 2);//happiness SetUInt32Value(UNIT_FIELD_MAXPOWER5, 1000000); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, GetNextLevelXP(getLevel())); SetUInt32Value(UNIT_FIELD_BYTES_1, 0 | (REBELIOUS << 8));//loyalty level SetUInt32Value(UNIT_TRAINING_POINTS, 0); //training points // Focus SetUInt32Value(UNIT_FIELD_POWER3, 100); SetUInt32Value(UNIT_FIELD_MAXPOWER3, 100); // 0x3 -> Enable pet rename. SetUInt32Value(UNIT_FIELD_BYTES_2, 1 | (0x28 << 8) | (0x3 << 16)); // Change the power type to FOCUS SetPowerType(POWER_TYPE_FOCUS); // create our spellz SetDefaultSpells(); } // Apply stats. ApplyStatsForLevel(); BaseDamage[0]=GetFloatValue(UNIT_FIELD_MINDAMAGE); BaseDamage[1]=GetFloatValue(UNIT_FIELD_MAXDAMAGE); BaseOffhandDamage[0]=GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE); BaseOffhandDamage[1]=GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE); BaseRangedDamage[0]=GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE); BaseRangedDamage[1]=GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, owner->GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE)); m_PartySpellsUpdateTimer = 0; m_PetNumber = static_cast(owner)->GeneratePetNumber(); SetUInt32Value(UNIT_FIELD_PETNUMBER, GetGUIDLow()); m_ExpireTime = expiretime; bExpires = m_ExpireTime > 0 ? true : false; if(!bExpires) { // Create PlayerPet struct (Rest done by UpdatePetInfo) PlayerPet *pp = new PlayerPet; pp->number = m_PetNumber; static_cast(owner)->AddPlayerPet(pp, pp->number); } InitializeMe(true); } Pet::Pet(uint32 high, uint32 low) : Creature(high, low) { m_PetXP = 0; Summon = false; memset(ActionBar, 0, sizeof(uint32)*10); m_AutoCombatSpell = 0; m_PartySpellsUpdateTimer = 0; m_HappinessTimer = PET_HAPPINESS_UPDATE_TIMER; m_LoyaltyTimer = PET_LOYALTY_UPDATE_TIMER; m_PetNumber = 0; m_State = PET_STATE_DEFENSIVE; m_Action = PET_ACTION_FOLLOW; bHasLoyalty = false; m_ExpireTime = 0; bExpires = false; m_Diet = 0; m_Action = PET_ACTION_FOLLOW; m_State = PET_STATE_DEFENSIVE; TP = 0; LoyaltyPts=0; } Pet::~Pet() { for(std::map::iterator itr = m_AISpellStore.begin(); itr != m_AISpellStore.end(); ++itr) delete itr->second; if(IsInWorld()) this->Remove(false, true, true); } void Pet::Update(uint32 time) { if(!m_Owner) return; Creature::Update(time); // passthrough if(bHasLoyalty && !bExpires) { //Happiness if(m_HappinessTimer == 0) { int32 val = GetUInt32Value(UNIT_FIELD_POWER5); //amount of burned happiness is loyalty_lvl depended int32 burn = HappinessTicks[GetLoyaltyLevel()-1]*125; //(ticks are 70/35/17/8/4/2 *1000 [per minute] ) /8 [per 7.5 sec] if((val - burn)<0) { val = 0; bExpires=true; m_ExpireTime=10000; //avoid loosing pet right after calling it } else val -= burn; SetUInt32Value(UNIT_FIELD_POWER5, val);// Set the value m_HappinessTimer = PET_HAPPINESS_UPDATE_TIMER;// reset timer } else { if(time > m_HappinessTimer) m_HappinessTimer = 0; else m_HappinessTimer -= time; } //Loyalty if(m_LoyaltyTimer==0 && GetHappinessState()!=NULL) { UpdateLoyalty(LoyaltyTicks[GetHappinessState()]);//loyalty tick is happiness state dependent m_LoyaltyTimer = PET_LOYALTY_UPDATE_TIMER; } else { if(time > m_LoyaltyTimer) m_LoyaltyTimer = 0; else m_LoyaltyTimer -= time; } } if(bExpires) { if(m_ExpireTime == 0) { // remove Dismiss(false); return; } else { if(time > m_ExpireTime) { m_ExpireTime = 0; } else { m_ExpireTime -= time; } } } } void Pet::SendSpellsToOwner() { int packetsize = (m_uint32Values[OBJECT_FIELD_ENTRY] != WATER_ELEMENTAL) ? (mSpells.size() * 4 + 20) : 64; WorldPacket * data = new WorldPacket(SMSG_PET_SPELLS, packetsize); *data << GetGUID(); *data << uint32(0x00000000);//unk1 *data << uint32(0x00000101);//unk2 // Send the actionbar for(uint32 i = 0; i < 10; ++i) { if(ActionBar[i] & 0x4000000) // Command *data << uint32(ActionBar[i]); else { if(uint16(ActionBar[i])) *data << uint16(ActionBar[i]) << GetSpellState(ActionBar[i]); else *data << uint16(0) << uint8(0) << uint8(i+5); } } // we don't send spells for the water elemental so it doesn't show up in the spellbook if(m_uint32Values[OBJECT_FIELD_ENTRY] != WATER_ELEMENTAL) { // Send the rest of the spells. *data << uint8(mSpells.size()); for(PetSpellMap::iterator itr = mSpells.begin(); itr != mSpells.end(); ++itr) *data << uint16(itr->first->Id) << uint16(itr->second); } *data << uint64(0); m_Owner->delayedPackets.add(data); } void Pet::SendNullSpellsToOwner() { WorldPacket data(8); data.SetOpcode(SMSG_PET_SPELLS); data << uint64(0); m_Owner->GetSession()->SendPacket(&data); } void Pet::InitializeSpells() { for(PetSpellMap::iterator itr = mSpells.begin(); itr != mSpells.end(); ++itr) { SpellEntry *info = itr->first; // Check that the spell isn't passive if(info->Attributes & 64) { // Cast on self.. Spell * sp = new Spell(this, info, true, false); SpellCastTargets targets(this->GetGUID()); sp->prepare(&targets); continue; } CreateAISpell(info); if(itr->second == 0xC100)//if autocast ON... { AI_Spell * sp = GetAISpellForSpellId(info->Id); if(sp) sp->procChance = PET_SPELL_AUTOCAST_CHANCE;//...set chance for it } } } void Pet::CreateAISpell(SpellEntry * info) { // Create an AI_Spell AI_Spell *sp = new AI_Spell; sp->agent = AGENT_SPELL; sp->entryId = GetEntry(); sp->floatMisc1 = 0; sp->maxrange = GetMaxRange(sSpellRange.LookupEntry(info->rangeIndex)); sp->minrange = GetMinRange(sSpellRange.LookupEntry(info->rangeIndex)); sp->Misc2 = 0; sp->procChance = 0; sp->spell = info; sp->spellType = STYPE_DAMAGE; sp->spelltargetType = TTYPE_SINGLETARGET; sp->cooldown = objmgr.GetPetSpellCooldown(info->Id); if(info->Effect[0] == SPELL_EFFECT_APPLY_AURA || info->Effect[0] == SPELL_EFFECT_APPLY_AREA_AURA) sp->spellType = STYPE_BUFF; if(info->EffectImplicitTargetA[0] == 24) { float radius = ::GetRadius(sSpellRadius.LookupEntry(info->EffectRadiusIndex[0])); sp->maxrange = radius; sp->spelltargetType = TTYPE_SOURCE; } m_AISpellStore[info->Id] = sp; m_aiInterface->addSpellToList(sp); } void Pet::LoadFromDB(Player* owner, PlayerPet * pi) { m_Owner = owner; m_OwnerGuid = m_Owner->GetGUID(); mPi = pi; creature_info = CreatureNameStorage.LookupEntry(mPi->entry); Create(pi->name.c_str(), owner->GetMapId(), owner->GetPositionX() + 2 , owner->GetPositionY() +2, owner->GetPositionZ(), owner->GetOrientation()); LoadValues(mPi->fields.c_str()); m_PetNumber = mPi->number; m_PetXP = mPi->xp; m_name = mPi->name; Summon = mPi->summon; SetIsPet(true); m_HappinessTimer = mPi->happinessupdate; m_LoyaltyTimer = mPi->loyaltyupdate; LoyaltyPts = mPi->loyaltypts; // Setup actionbar uint32 pos = 0; string::size_type sp = mPi->actionbar.find(","); string::size_type lp = 0; while(sp != string::npos) { uint32 spell = atol(mPi->actionbar.substr(lp, sp).c_str()); ActionBar[pos] = spell; ++pos; lp = sp+1; sp = mPi->actionbar.find(",", lp); } SetIsPet(true); InitializeMe(false); bExpires = false; if(pi->autocastspell && Summon)//hunter pet autocast flag are set in InitializeSpells { AI_Spell * sp = GetAISpellForSpellId(pi->autocastspell); if(sp) { sp->procChance = PET_SPELL_AUTOCAST_CHANCE; //m_aiInterface->disable_melee = true; } } if(m_Owner && getLevel() > m_Owner->getLevel()) { SetUInt32Value(UNIT_FIELD_LEVEL, m_Owner->getLevel()); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, GetNextLevelXP(m_Owner->getLevel())); ApplyStatsForLevel(); } // Nuke auras for(uint32 x = UNIT_FIELD_AURA_01; x <= UNIT_FIELD_AURA_55; x++) SetUInt32Value(x, 0); } void Pet::InitializeMe(bool first) { // set up ai and shit GetAIInterface()->Init(this,AITYPE_PET,MOVEMENTTYPE_NONE,m_Owner); GetAIInterface()->SetUnitToFollow(m_Owner); GetAIInterface()->SetFollowDistance(3.0f); SetCreatureName(CreatureNameStorage.LookupEntry(GetEntry())); m_Owner->SetSummon(this); m_Owner->SetUInt64Value(UNIT_FIELD_SUMMON, this->GetGUID()); SetUInt32Value(UNIT_FIELD_PETNUMBER, GetGUIDLow()); SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, (uint32)time(NULL)); myFamily = sCreatureFamilyStore.LookupEntry(creature_info->Family); bHasLoyalty = m_Owner->getClass() == HUNTER ? true : false; SetPetDiet(); _setFaction(); m_State = 1; // dont set agro on spawn if(GetEntry() == 416) m_aiInterface->disable_melee = true; PushToWorld(m_Owner->GetMapMgr()); //InitializeSpells(); // Load our spells if(Summon) { SetDefaultSpells(); // Adds parent +frost spell damage if(m_uint32Values[OBJECT_FIELD_ENTRY] == WATER_ELEMENTAL) { float parentfrost = (float)m_Owner->GetDamageDoneMod(SCHOOL_FROST); parentfrost *= 0.40f; ModDamageDone[SCHOOL_FROST] = (uint32)parentfrost; } } else { // Pull from database... :/ QueryResult * query = CharacterDatabase.Query("SELECT * FROM playerpetspells WHERE ownerguid=%u and petnumber=%u", m_Owner->GetGUIDLow(), m_PetNumber); if(query) { do { Field * f = query->Fetch(); SpellEntry* spell = sSpellStore.LookupEntry(f[2].GetUInt32()); uint16 flags = f[3].GetUInt16(); mSpells.insert ( make_pair( spell, flags ) ); } while(query->NextRow()); } delete query; } InitializeSpells(); if(first) { // Set up default actionbar SetDefaultActionbar(); } SendSpellsToOwner(); sEventMgr.AddEvent(this, &Pet::Update, (uint32)100, EVENT_PET_UPDATE, 100, 0,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); // set to active if(!bExpires) UpdatePetInfo(false); } void Pet::UpdatePetInfo(bool bSetToOffline) { if(bExpires) return; // don't update expiring pets PlayerPet *pi = m_Owner->GetPlayerPet(m_PetNumber); pi->active = !bSetToOffline; pi->entry = GetEntry(); std::stringstream ss; for( uint32 index = 0; index < UNIT_END; index ++ ) ss << GetUInt32Value(index) << " "; pi->fields = ss.str(); pi->name = GetName(); pi->number = m_PetNumber; pi->xp = m_PetXP; pi->level = GetUInt32Value(UNIT_FIELD_LEVEL); pi->happiness = GetUInt32Value(UNIT_FIELD_POWER5); pi->happinessupdate = m_HappinessTimer; pi->loyaltypts = LoyaltyPts; pi->loyaltyupdate = m_LoyaltyTimer; pi->loyaltylvl = GetLoyaltyLevel(); // save actionbar ss.rdbuf()->str(""); for(uint32 i = 0; i < 10; ++i) { ss << ActionBar[i] << ","; } pi->actionbar = ss.str(); pi->summon = Summon; /*AI_Spell * sp = m_aiInterface->GetDefaultSpell(); if(sp) { if(sp->agent == AGENT_SPELL) pi->autocastspell = sp->spell->Id; else pi->autocastspell = 0; }*/ //FIXME pi->autocastspell=0; } void Pet::Dismiss(bool bSafeDelete)//Abandon pet { // Delete any petspells for us. if(!Summon && m_Owner) { CharacterDatabase.Execute("DELETE FROM playerpetspells WHERE ownerguid=%u AND petnumber=%u", m_Owner->GetGUIDLow(), m_PetNumber); } if(m_Owner) m_Owner->RemovePlayerPet(m_PetNumber); // find out playerpet entry, delete it Remove(bSafeDelete, false, true); } void Pet::Remove(bool bSafeDelete, bool bUpdate, bool bSetOffline) { if(m_Owner) { // remove association with player m_Owner->SetUInt64Value(UNIT_FIELD_SUMMON, 0); if(bUpdate) { if(!bExpires) UpdatePetInfo(bSetOffline); if(!IsSummon()) m_Owner->_SavePet();//not perfect but working } m_Owner->SetSummon(NULL); SendNullSpellsToOwner(); } ClearPetOwner(); /* if(bSafeDelete) else*/ /* PetSafeDelete();*/ // has to be next loop - reason because of RemoveFromWorld, iterator gets broke. if(IsInWorld() && Active) Deactivate(m_mapMgr); sEventMgr.AddEvent(this, &Pet::PetSafeDelete, EVENT_CREATURE_SAFE_DELETE, 1, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } void Pet::PetSafeDelete() { if(this->IsInWorld()) { // remove from world, and delete RemoveFromWorld(false); } //sEventMgr.AddEvent(World::getSingletonPtr(), &World::DeleteObject, ((Object*)this), EVENT_CREATURE_SAFE_DELETE, 1000, 1); Creature::SafeDelete(); } void Pet::DelayedRemove(bool bTime, bool bDeath) { m_Owner = objmgr.GetPlayer(m_OwnerGuid); if(bTime) { if(GetUInt32Value(UNIT_CREATED_BY_SPELL) > 0 || bDeath) Dismiss(true); // remove us.. else Remove(true, true, true); } else sEventMgr.AddEvent(this, &Pet::DelayedRemove, true, bDeath, EVENT_PET_DELAYED_REMOVE, PET_DELAYED_REMOVAL_TIME, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } void Pet::GiveXP(uint32 xp) { if(!m_Owner || getLevel() >= myFamily->maxlevel || getLevel() > m_Owner->getLevel()) return; xp += m_uint32Values[UNIT_FIELD_PETEXPERIENCE]; uint32 nxp = m_uint32Values[UNIT_FIELD_PETNEXTLEVELEXP]; bool changed = false; while(xp >= nxp) { ModUInt32Value(UNIT_FIELD_LEVEL, 1); xp -= nxp; nxp = GetNextLevelXP(m_uint32Values[UNIT_FIELD_LEVEL]); changed = true; } SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, xp); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, nxp); if(changed) ApplyStatsForLevel(); } uint32 Pet::GetNextLevelXP(uint32 currentlevel) { uint32 level = currentlevel + 1; uint32 nextLvlXP = 0; if( level > 0 && level <= 30 ) { nextLvlXP = ((int)((((double)(8 * level * ((level * 5) + 45)))/100)+0.5))*100; } else if( level == 31 ) { nextLvlXP = ((int)((((double)(((8 * level) + 3) * ((level * 5) + 45)))/100)+0.5))*100; } else if( level == 32 ) { nextLvlXP = ((int)((((double)(((8 * level) + 6) * ((level * 5) + 45)))/100)+0.5))*100; } else { nextLvlXP = ((int)((((double)(((8 * level) + ((level - 30) * 5)) * ((level * 5) + 45)))/100)+0.5))*100; } double xp = double(nextLvlXP) / 4.0; return FL2UINT(xp); } void Pet::SetDefaultSpells() { if(Summon) { // this one's easy :p we just pull em from the owner. map >::iterator it1; set::iterator it2; it1 = m_Owner->SummonSpells.find(GetEntry()); if(it1 != m_Owner->SummonSpells.end()) { it2 = it1->second.begin(); for(; it2 != it1->second.end(); ++it2) { AddSpell((*it2)); } } } else { uint32 Line = GetCreatureName()->SpellDataID; if(Line) { CreatureSpellDataEntry * SpellData = CreatureSpellDataStore::getSingleton().LookupEntry(Line); if(SpellData) for(uint32 i = 0; i < 3; ++i) if(SpellData->Spells[i] != 0) AddSpell(SpellData->Spells[i]); //add spell to pet } } } void Pet::AddSpell(SpellEntry * sp, bool putInBar) { // Cast on self if we're a passive spell if(sp->Attributes & 64) { if(IsInWorld()) { Spell * spell = new Spell(this, sp, true, false); SpellCastTargets targets(this->GetGUID()); spell->prepare(&targets); mSpells[sp] = 0x0100; } } else { // Active spell add to the actionbar. for(int i = 0; i < 10; ++i) { if(ActionBar[i] == 0) { ActionBar[i] = sp->Id; break; } } mSpells[sp] = DEFAULT_SPELL_STATE; // Create the AI_Spell CreateAISpell(sp); } if(IsInWorld() && sp->Effect[0] == SPELL_EFFECT_APPLY_AREA_AURA) { // Autocast by default SetSpellState(sp, PET_ACTION_SPELL); // Cast it. CastSpell(this, sp, true); } else if(putInBar) { // Add to the actionbar. for(int i = 0; i < 10; ++i) { if(ActionBar[i] == 0) { ActionBar[i] = sp->Id; break; } } } if(IsInWorld()) SendSpellsToOwner(); } void Pet::SetSpellState(SpellEntry* sp, uint16 State) { PetSpellMap::iterator itr = mSpells.find(sp); if(itr == mSpells.end()) return; itr->second = State; } uint16 Pet::GetSpellState(SpellEntry* sp) { PetSpellMap::iterator itr = mSpells.find(sp); if(itr == mSpells.end()) return DEFAULT_SPELL_STATE; return itr->second; } void Pet::SetDefaultActionbar() { // Set up the default actionbar. ActionBar[0] = PET_SPELL_ATTACK; ActionBar[1] = PET_SPELL_FOLLOW; ActionBar[2] = PET_SPELL_STAY; // Fill up 4 slots with our spells if(mSpells.size() > 0) { PetSpellMap::iterator itr = mSpells.begin(); uint32 pos = 0; for(; itr != mSpells.end() && pos < 4; ++itr, ++pos) ActionBar[3+pos] = itr->first->Id; } ActionBar[7] = PET_SPELL_AGRESSIVE; ActionBar[8] = PET_SPELL_DEFENSIVE; ActionBar[9] = PET_SPELL_PASSIVE; } void Pet::RemoveSpell(SpellEntry * sp) { mSpells.erase(sp); map::iterator itr = m_AISpellStore.find(sp->Id); if(itr != m_AISpellStore.end()) { delete itr->second; for(list::iterator it = m_aiInterface->m_spells.begin(); it != m_aiInterface->m_spells.end(); ++it) { if((*it) == itr->second) { m_aiInterface->m_spells.erase(it); m_aiInterface->CheckNextSpell(itr->second); break; } } m_AISpellStore.erase(itr); } } void Pet::Rename(string NewName) { m_name = NewName; // update petinfo UpdatePetInfo(false); // update timestamp to force a re-query SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); } void Pet::ApplySummonLevelAbilities() { uint32 level = m_uint32Values[UNIT_FIELD_LEVEL]; double pet_level = (double)level; // Determine our stat index. int stat_index = -1; //float scale = 1; bool has_mana = true; switch(m_uint32Values[OBJECT_FIELD_ENTRY]) { case 416: //Imp stat_index = 0; m_aiInterface->disable_melee = true; break; case 1860: //VoidWalker stat_index = 1; break; case 1863: //Succubus stat_index = 2; break; case 417: //Felhunter stat_index = 3; break; case 11859: // Doomguard case 89: // Infernal case 17252: // Felguard stat_index = 4; break; /*case 11859: // Doomguard stat_index = 4; break; case 89: // Infernal stat_index = 5; has_mana = false; break; case 17252: // Felguard stat_index = 6; break;*/ case 510: // Mage's water elemental stat_index = 5; break; } if(m_uint32Values[OBJECT_FIELD_ENTRY] == 89) has_mana = false; if(stat_index < 0) { sLog.outError("PETSTAT: No stat index found for entry %u, `%s`!", GetEntry(), creature_info->Name); return; } /* ---------------------------- ------- Warlock Pets ------- ---------------------------- pet_str = base_str + pet_lvl * mod_str pet_agi = base_agi + pet_lvl * mod_agi pet_sta = base_sta + pet_lvl * mod_sta + pet_sta_bonus pet_int = base_int + pet_lvl * mod_int + pet_int_bonus pet_spr = base_spr + pet_lvl * mod_spr pet_pwr = base_pwr + pet_lvl * mod_pwr pet_arm = base_arm + pet_lvl * mod_arm + pet_arm_bonus pet_sta_bonus = 0.3 * plyr_sta + sta_buffs pet_int_bonus = 0.3 * plyr_int + int_buffs pet_arm_bonus = 0.35 * plyr_arm + arm_buffs --[Imp]-- base_str = 18.0 mod_str = 2.0 base_agi = 20.2 mod_agi = 0.2 base_sta = 19.5 mod_sta = 0.5 base_int = 21.0 mod_int = 3.0 base_spr = 19.0 mod_spr = 4.0 base_pwr = 9.0 mod_pwr = 1.0 base_arm = 47.0 mod_arm = 13.0 --[Voidwalker]-- base_str = 19.0 mod_str = 2.0 base_agi = 15.0 mod_agi = 1.0 base_sta = -13.0 mod_sta = 4.0 base_int = 17.3 mod_int = 1.7 base_spr = 9.0 mod_spr = 2.0 base_pwr = -16.0 mod_pwr = 4.0 base_arm = 177.0 mod_arm = 50.0 --[Succubus]-- base_str = 2.0 mod_str = 2.0 base_agi = 16.0 mod_agi = 1.0 base_sta = -17.0 mod_sta = 4.5 base_int = 9.0 mod_int = 2.0 base_spr = 12.0 mod_spr = 1.3 base_pwr = -26.0 mod_pwr = 4.0 base_arm = 125.0 mod_arm = 23.0 --[Felhunter]-- base_str = -3.0 mod_str = 2.0 base_agi = 16.0 mod_agi = 1.0 base_sta = 36.0 mod_sta = 3.0 base_int = 9.0 mod_int = 2.0 base_spr = 21.0 mod_spr = 1.0 base_pwr = -26.0 mod_pwr = 4.0 base_arm = -910.0 mod_arm = 57.0 --[Doomguard]-- base_str = 9.0 mod_str = 2.0 base_agi = -5.0 mod_agi = 1.5 base_sta = -66.0 mod_sta = 5.0 base_int = 10.0 mod_int = 1.0 base_spr = 30.0 mod_spr = 2.0 base_pwr = -62.0 mod_pwr = 5.0 base_arm = -187.0 mod_arm = 65.0 --[Infernal]-- base_str = -14.0 mod_str = 2.5 base_agi = -20.0 mod_agi = 2.0 base_sta = -44.0 mod_sta = 5.0 base_int = 20.0 mod_int = 0.5 base_spr = -40.0 mod_spr = 2.0 base_pwr = -48.0 mod_pwr = 5.0 base_arm = 1605.0 mod_arm = 50.0 --[Felguard]-- base_str = -39.0 mod_str = 3.0 base_agi = 7.0 mod_agi = 1.5 base_sta = -14.0 mod_sta = 6.0 base_int = 82.0 mod_int = 2.0 base_spr = -12.0 mod_spr = 2.0 base_pwr = -68.0 mod_pwr = 5.5 base_arm = 1208.0 mod_arm = 47.0 */ // Imp,Voidwalker,Succubus,Felhunter,Doomguard,Infernal,Felguard /*static double R_base_str[7] = { 18.1884058,-15,-15,-15,-15, -14, -39 }; static double R_mod_str[7] = { 1.811594203,2.4,2.4,2.4,2.4, 2.5, 3 }; static double R_base_agi[7] = { 20.2, 15, 16, 16, -5, -20, 7 }; static double R_mod_agi[7] = { 0.2, 1, 1, 1, 1.5, 2, 1.5 }; static double R_base_sta[7] = { 19.5, -13, -17, 36, -66, -44, -14 }; static double R_mod_sta[7] = { 0.5, 4, 4.5, 3, 5, 5, 6 }; static double R_base_int[7] = { 21, 17.3, 9, 9, 10, 20, 82 }; static double R_mod_int[7] = { 3, 1.7, 2, 2, 1, 0.5, 2 }; static double R_base_spr[7] = { 19, 9, 12, 21, 30, -40, -12 }; static double R_mod_spr[7] = { 4, 2, 1.3, 1, 2, 2, 2 }; static double R_base_pwr[7] = { 9, -16, -26, -26, -62, -48, -68 }; static double R_mod_pwr[7] = { 1, 4, 4, 4, 5, 5, 5.5 }; static double R_base_arm[7] = { 47, 177, 125, -910, -187, 1605, 1208 }; static double R_mod_arm[7] = { 13, 50, 23, 57, 65, 50, 47 }; double base_str = R_base_str[stat_index]; double mod_str = R_mod_str[stat_index]; double base_agi = R_base_agi[stat_index]; double mod_agi = R_mod_agi[stat_index]; double base_sta = R_base_sta[stat_index]; double mod_sta = R_mod_sta[stat_index]; double base_int = R_base_int[stat_index]; double mod_int = R_mod_int[stat_index]; double base_spr = R_base_spr[stat_index]; double mod_spr = R_mod_spr[stat_index]; double base_pwr = R_base_pwr[stat_index]; double mod_pwr = R_mod_pwr[stat_index]; double base_arm = R_base_arm[stat_index]; double mod_arm = R_mod_arm[stat_index]; // Calculate bonuses double pet_sta_bonus = 0.3 * (double)m_Owner->BaseStats[STAT_STAMINA]; double pet_int_bonus = 0.3 * (double)m_Owner->BaseStats[STAT_INTELLECT]; double pet_arm_bonus = 0.35 * (double)m_Owner->BaseResistance[0]; double pet_str = base_str + pet_level * mod_str; double pet_agi = base_agi + pet_level * mod_agi; double pet_sta = base_sta + pet_level * mod_str + pet_sta_bonus; double pet_int = base_int + pet_level * mod_int + pet_int_bonus; double pet_spr = base_spr + pet_level * mod_spr; double pet_pwr = base_pwr + pet_level * mod_pwr; double pet_arm = base_arm + pet_level * mod_arm; // Calculate actual values. BaseStats[STAT_STRENGTH] = FL2UINT(pet_str); BaseStats[STAT_AGILITY] = FL2UINT(pet_agi); BaseStats[STAT_STAMINA] = FL2UINT(pet_sta); BaseStats[STAT_INTELLECT] = FL2UINT(pet_int); BaseStats[STAT_SPIRIT] = FL2UINT(pet_spr); for(uint32 x = 0; x < 5; ++x) CalcStat(x); // Apply armor and attack power. SetUInt32Value(UNIT_FIELD_ATTACK_POWER, FL2UINT(pet_pwr)); BaseResistance[0] = FL2UINT(pet_arm); CalcResistance(0); CalcDamage(); // Calculate health / mana double health = pet_sta * 10; double mana = has_mana ? (base_int * 15) : 0.0; SetUInt32Value(UNIT_FIELD_BASE_HEALTH, FL2UINT(health)); SetUInt32Value(UNIT_FIELD_BASE_MANA, FL2UINT(mana));*/ static double R_base_str[6] = {18.1884058, -15, -15, -15, -15, -15}; static double R_mod_str[6] = {1.811594203, 2.4, 2.4, 2.4, 2.4, 2.4}; static double R_base_agi[6] = {19.72463768, -1.25, -1.25, -1.25, -1.25, -1.25}; static double R_mod_agi[6] = {0.275362319, 1.575, 1.575, 1.575, 1.575, 1.575}; static double R_base_sta[6] = {17.23188406, -17.75, -17.75, -17.75, -17.75, 0}; static double R_mod_sta[6] = {2.768115942, 4.525, 4.525, 4.525, 4.525, 4.044}; static double R_base_int[6] = {19.44927536, 12.75, 12.75, 12.75, 12.75, 20}; static double R_mod_int[6] = {4.550724638, 1.875, 1.875, 1.875, 1.875, 2.8276}; static double R_base_spr[6] = {19.52173913, -2.25, -2.25, -2.25, -2.25, 20.5}; static double R_mod_spr[6] = {3.47826087, 1.775, 1.775, 1.775, 1.775, 3.5}; static double R_base_pwr[6] = {7.202898551, -101, -101, -101, -101, -101}; static double R_mod_pwr[6] = {2.797101449, 6.5, 6.5, 6.5, 6.5, 6.5}; static double R_base_armor[6] = {-11.69565217, -702, -929.4, -1841.25, -1157.55, -1000}; static double R_mod_armor[6] = {31.69565217, 139.6, 74.62, 89.175, 101.1316667, 100}; static double R_pet_sta_to_hp[6] = {6.405982906, 15.91304348, 7.956521739, 10.79813665, 11.55590062, 10.0}; static double R_base_min_dmg[6] = {0.550724638, 4.566666667, 26.82, 29.15, 20.17888889, 20}; static double R_mod_min_dmg[6] = {1.449275362, 1.433333333, 2.18, 1.85, 1.821111111, 1}; static double R_base_max_dmg[6] = {1.028985507, 7.133333333, 36.16, 39.6, 27.63111111, 20}; static double R_mod_max_dmg[6] = {1.971014493, 1.866666667, 2.84, 2.4, 2.368888889, 1.1}; double base_str = R_base_str[stat_index]; double mod_str = R_mod_str[stat_index]; double base_agi = R_base_agi[stat_index]; double mod_agi = R_mod_agi[stat_index]; double base_sta = R_base_sta[stat_index]; double mod_sta = R_mod_sta[stat_index]; double base_int = R_base_int[stat_index]; double mod_int = R_mod_int[stat_index]; double base_spr = R_base_spr[stat_index]; double mod_spr = R_mod_spr[stat_index]; double base_pwr = R_base_pwr[stat_index]; double mod_pwr = R_mod_pwr[stat_index]; double base_armor = R_base_armor[stat_index]; double mod_armor = R_mod_armor[stat_index]; double base_min_dmg = R_base_min_dmg[stat_index]; double mod_min_dmg = R_mod_min_dmg[stat_index]; double base_max_dmg = R_base_max_dmg[stat_index]; double mod_max_dmg = R_mod_max_dmg[stat_index]; double pet_sta_to_hp = R_pet_sta_to_hp[stat_index]; // Calculate bonuses double pet_sta_bonus = 0.3 * (double)m_Owner->BaseStats[STAT_STAMINA]; // + sta_buffs double pet_int_bonus = 0.3 * (double)m_Owner->BaseStats[STAT_INTELLECT]; // + int_buffs double pet_arm_bonus = 0.35 * (double)m_Owner->BaseResistance[0]; // + arm_buffs double pet_str = base_str + pet_level * mod_str; double pet_agi = base_agi + pet_level * mod_agi; double pet_sta = base_sta + pet_level * mod_sta + pet_sta_bonus; double pet_int = base_int + pet_level * mod_int + pet_int_bonus; double pet_spr = base_spr + pet_level * mod_spr; double pet_pwr = base_pwr + pet_level * mod_pwr; double pet_arm = base_armor + pet_level * mod_armor + pet_arm_bonus; // Calculate values BaseStats[STAT_STRENGTH] = FL2UINT(pet_str); BaseStats[STAT_AGILITY] = FL2UINT(pet_agi); BaseStats[STAT_STAMINA] = FL2UINT(pet_sta); BaseStats[STAT_INTELLECT] = FL2UINT(pet_int); BaseStats[STAT_SPIRIT] = FL2UINT(pet_spr); double pet_min_dmg = base_min_dmg + pet_level * mod_min_dmg; double pet_max_dmg = base_max_dmg + pet_level * mod_max_dmg; BaseDamage[0] = FL2UINT(pet_min_dmg); BaseDamage[1] = FL2UINT(pet_max_dmg); for(uint32 x = 0; x < 5; ++x) CalcStat(x); // Apply armor and attack power. SetUInt32Value(UNIT_FIELD_ATTACK_POWER, FL2UINT(pet_pwr)); BaseResistance[0] = FL2UINT(pet_arm); CalcResistance(0); CalcDamage(); // Calculate health / mana double health = pet_sta * pet_sta_to_hp; double mana = has_mana ? (pet_int * 15) : 0.0; SetUInt32Value(UNIT_FIELD_BASE_HEALTH, FL2UINT(health)); SetUInt32Value(UNIT_FIELD_BASE_MANA, FL2UINT(mana)); } void Pet::ApplyPetLevelAbilities() { uint32 level = m_uint32Values[UNIT_FIELD_LEVEL]; double dlevel = (double)level; /* ----------[Pets]---------- Family pet_mod_sta pet_mod_arm pet_mod_dps (1) Wolf 1.00 1.05 1.00 (2) Cat 0.98 1.00 1.10 (3) Spider 1.00 1.00 1.07 (4) Bear 1.08 1.05 0.91 (5) Boar 1.04 1.09 0.90 (6) Crocolisk 0.95 1.10 1.00 (7) Carrion Bird 1.00 1.05 1.00 (8) Crab 0.96 1.13 0.95 (9) Gorilla 1.04 1.00 1.02 (10) (11) Raptor 0.95 1.03 1.10 (12) Tallstrider 1.05 1.00 1.00 (13) (14) (15) Felhunter (16) Voidwalker (17) Succubus (18) (19) Doomguard (20) Scorpid 1.00 1.10 0.94 (21) Turtle 1.00 1.13 0.90 (22) (23) Imp (24) Bat 1.00 1.00 1.07 (25) Hyena 1.00 1.05 1.00 (26) Owl 1.00 1.00 1.07 (27) Wind Serpent 1.00 1.00 1.07 (28) Remote Control (29) Felguard (30) Dragonhawk 1.00 1.00 1.00 (31) Ravager 0.93 1.05 1.10 (32) Warp Stalker 1.00 1.05 0.94 (33) Spore Bat 1.00 1.00 1.00 (34) Nether Ray 1.10 0.90 1.03 (35) Serpent 1.00 1.00 1.00 */ static double R_pet_mod_sta[36] = { 0, 1, 0.98, 1, 1.08, 1.04, 0.95, 1, 0.96, 1.04, 0, 0.95, 1.05, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0.93, 1, 1, 1.1, 1 }; static double R_pet_mod_arm[36] = { 0, 1.05, 1, 1, 1.05, 1.09, 1.1, 1.05, 1.13, 1, 0, 1.03, 1, 0, 0, 0, 0, 0, 0, 0, 1.1, 1.13, 0, 0, 1, 1.05, 1, 1, 0, 0, 1, 1.05, 1.05, 1, 0.9, 1 }; static double R_pet_mod_dps[36] = { 0, 1, 1.10, 1.07, 0.91, 0.9, 1, 1, 0.95, 1.02, 0, 1.1, 1, 0, 0, 0, 0, 0, 0, 0, 0.94, 0.9, 0, 0, 1.07, 1, 1.07, 1.07, 0, 0, 1, 1.1, 0.94, 1, 1.03, 1 }; double pet_mod_sta = 1, pet_mod_arm = 1, pet_mod_dps = 1; if(creature_info->Family > 35 || R_pet_mod_sta[creature_info->Family] == 0) sLog.outError("PETSTAT: Creature family %u [%s] has missing data. Assuming to be 1.", creature_info->Family); else { pet_mod_sta = R_pet_mod_sta[creature_info->Family]; pet_mod_arm = R_pet_mod_arm[creature_info->Family]; pet_mod_dps = R_pet_mod_dps[creature_info->Family]; } // Calculate Bonuses double pet_sta_bonus = 0.3 * (double)m_Owner->BaseStats[STAT_STAMINA]; double pet_arm_bonus = 0.35 * (double)m_Owner->BaseResistance[0]; // Armor double pet_ap_bonus = 0.22 * (double)m_Owner->GetUInt32Value(UNIT_FIELD_ATTACK_POWER); // Calculate HP double pet_hp = ( ( ( 0.6 * dlevel * dlevel + 10.6 * dlevel + 33 ) + ( pet_sta_bonus * 10 ) ) * pet_mod_sta); double pet_armor = ( ( -75 + 50 * dlevel ) * pet_mod_arm + pet_arm_bonus ); double pet_attack_power = ( ( ( 20 * dlevel) - 60 ) + pet_ap_bonus ) * pet_mod_dps; if(pet_attack_power <= 0.0f) pet_attack_power = 1; if(pet_armor <= 0.0f) pet_armor = 1; // Set base values. SetUInt32Value(UNIT_FIELD_BASE_HEALTH, FL2UINT(pet_hp)); BaseResistance[0] = FL2UINT(pet_armor); CalcResistance(0); // Calculate damage. SetUInt32Value(UNIT_FIELD_ATTACK_POWER, FL2UINT(pet_attack_power)); CalcDamage(); // These are just for visuals, no other actual purpose. BaseStats[0] = uint32(20+getLevel()*1.55); BaseStats[1] = uint32(20+getLevel()*0.64); BaseStats[3] = uint32(20+getLevel()*0.18); BaseStats[4] = uint32(20+getLevel()*0.36); // Reverse the health value to calculate stamina BaseStats[STAT_STAMINA] = FL2UINT(pet_hp / 10); for(uint32 x = 0; x < 5; ++x) CalcStat(x); UpdateTP(); } void Pet::ApplyStatsForLevel() { if(m_uint32Values[UNIT_CREATED_BY_SPELL]) // Summon ApplySummonLevelAbilities(); else ApplyPetLevelAbilities(); // Apply common stuff float pet_level = m_uint32Values[UNIT_FIELD_LEVEL]; // Apply scale for this family. float level_diff = myFamily->maxlevel - myFamily->minlevel; float scale_diff = myFamily->maxsize - myFamily->minsize; float factor = scale_diff / level_diff; float scale = factor * pet_level + myFamily->minsize; SetFloatValue(OBJECT_FIELD_SCALE_X, scale); // Apply health fields. SetUInt32Value(UNIT_FIELD_HEALTH, m_uint32Values[UNIT_FIELD_BASE_HEALTH]); SetUInt32Value(UNIT_FIELD_MAXHEALTH, m_uint32Values[UNIT_FIELD_BASE_HEALTH]); SetUInt32Value(UNIT_FIELD_POWER1, m_uint32Values[UNIT_FIELD_BASE_MANA]); SetUInt32Value(UNIT_FIELD_MAXPOWER1, m_uint32Values[UNIT_FIELD_BASE_MANA]); } uint16 Pet::SpellTP(uint32 spellId) { //returns required training points for spell skilllinespell *sk = objmgr.GetSpellSkill(spellId); if(sk) return sk->reqTP; return 0; } uint16 Pet::GetUsedTP() { //goes through all pet spells and sums training points int16 sumTP = 0; if(mSpells.size() > 0) { PetSpellMap::iterator itr = mSpells.begin(); for(; itr != mSpells.end(); ++itr) { //higher spell ranks takes TP incrementally from lower rank, so lets sum the highest rank spells if(itr->first->Id == GetHighestRankSpell(itr->first->Id)) sumTP += SpellTP(itr->first->Id); } } return sumTP; } bool Pet::CanLearnSpellTP(uint32 spellId) { //checks if pet has enough TPs to learn new spell //higher spell ranks take TPs incrementallly, so we need this calculation return SpellTP(spellId) - SpellTP(GetHighestRankSpell(spellId)) > TP ? false : true; } void Pet::UpdateTP() { //update pets TP //formula: TP = level*(loyaltyLvl - 1) - usedTP //http://petopia.brashendeavors.net/html/articles/skills_main.shtml if(!m_Owner) return; int16 pts = getLevel()*(GetLoyaltyLevel()-1)-GetUsedTP(); TP = pts; SetUInt32Value(UNIT_TRAINING_POINTS, pts < 0?(-pts & 0xffff):(pts<<16));//uff, works, but has anybody better idea? } HappinessState Pet::GetHappinessState() { //gets happiness state from happiness points if(GetUInt32Value(UNIT_FIELD_POWER5)=2*PET_HAPPINESS_UPDATE_VALUE) return HAPPY; else return CONTENT; } void Pet::AddPetSpellToOwner(uint32 spellId) { uint32 line = 0; if (objmgr.GetSpellSkill(spellId)) line = objmgr.GetSpellSkill(spellId)->skilline; //exit if owner hasn't Beast training ability (id 5149) if(!m_Owner || !m_Owner->HasSpell(5149) || !line) return; //find appropriate teaching spell... vector* lst = objmgr.GetTrainerPetSpellsForLine(line); TrainerSpell * sp; if(lst) for(vector::iterator itr = lst->begin(); itr != lst->end(); ++itr) { sp = *itr; if(spellId==sp->SpellID) if(m_Owner->HasSpell(sp->TeachingSpellID)) return; else { //...and add it to pet owner to be able teach other pets m_Owner->addSpell(sp->TeachingSpellID); return; } } } uint32 Pet::GetHighestRankSpell(uint32 spellId) { //get the highest rank of spell from known spells SpellEntry *sp = sSpellStore.LookupEntry(spellId); SpellEntry *tmp = 0; if(sp && mSpells.size() > 0) { PetSpellMap::iterator itr = mSpells.begin(); for(; itr != mSpells.end(); ++itr) if(sp->NameHash == itr->first->NameHash) if((!tmp || tmp->RankNumber < itr->first->RankNumber)) tmp = itr->first; } return tmp ? tmp->Id : 0; } void Pet::UpdateLoyalty(char pts) { //updates loyalty_pts and loyalty lvl if needed if((LoyaltyPts + pts) < 0) LoyaltyPts = 0; else if ((LoyaltyPts + pts) > BEST_FRIEND * PET_LOYALTY_LVL_RANGE) LoyaltyPts = BEST_FRIEND * PET_LOYALTY_LVL_RANGE; else LoyaltyPts += pts; char curLvl = GetLoyaltyLevel(); char newLvl = LoyaltyPts / PET_LOYALTY_LVL_RANGE; //result can be 0~7 ... newLvl < BEST_FRIEND ? newLvl += 1 : newLvl = BEST_FRIEND;//...so make it 1~6 if (newLvl != curLvl) { SetUInt32Value(UNIT_FIELD_BYTES_1, 0 | (newLvl << 8)); UpdateTP(); } }